home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / pcl / src-16f.lha / compiler / disassem.lisp < prev    next >
Encoding:
Text File  |  1992-05-19  |  120.7 KB  |  3,411 lines

  1. ;;; -*- Package: DISASSEM -*-
  2. ;;;
  3. ;;; **********************************************************************
  4. ;;; This code was written for the use of the CMU Common Lisp project at
  5. ;;; Carnegie Mellon University, and has been placed in the public domain.
  6. ;;; If you want to use this code or any part of CMU Common Lisp, please contact
  7. ;;; Scott Fahlman or slisp-group@cs.cmu.edu.
  8. ;;;
  9. (ext:file-comment
  10.   "$Header: disassem.lisp,v 1.7 92/04/14 03:00:54 wlott Exp $")
  11. ;;;
  12. ;;; **********************************************************************
  13. ;;;
  14. ;;; Machine independent disassembler for CMU Common Lisp
  15. ;;;
  16. ;;; Written by Miles Bader <miles@cogsci.ed.ac.uk>
  17. ;;;
  18.  
  19. (in-package 'disassem)
  20.  
  21. (export '(;; for defining the instruction set
  22.       set-disassem-params
  23.       gen-inst-format-decl-form gen-field-type-decl-form gen-inst-decl-form
  24.       specialize
  25.  
  26.       ;; main user entry-points
  27.       disassemble
  28.       disassemble-memory
  29.       disassemble-function
  30.       disassemble-code-component
  31.  
  32.       ;; some variables to set
  33.       *opcode-column-width*
  34.       *note-column*
  35.  
  36.       ;; slightly lower level entry-points
  37.       create-dstate set-dstate-segment
  38.       get-function-segments get-code-segments label-segments
  39.       disassemble-segments disassemble-segment
  40.       set-address-printing-range
  41.       add-hook add-note-hook add-comment-hook
  42.  
  43.       ;; segment type
  44.       segment seg-start seg-length seg-debug-function
  45.  
  46.       ;; for printing instructions
  47.       print-field
  48.       print-inst-using
  49.       print-inst
  50.  
  51.       ;; decoding a bit-pattern
  52.       sap-ref-dchunk
  53.       get-inst-space
  54.       find-inst
  55.  
  56.       ;; getting at the dstate (usually from mach-dep code)
  57.       disassem-state dstate-curpos dstate-nextpos dstate-code
  58.       dstate-segment-start dstate-segment-length dstate-segment-sap
  59.       dstate-get-prop
  60.  
  61.       ;; random types
  62.       params inst inst-format field-type
  63.  
  64.       ;; 
  65.       arg-value
  66.       sign-extend
  67.  
  68.       ;; making handy margin notes
  69.       note
  70.       note-code-constant
  71.       maybe-note-nil-indexed-symbol-slot-ref
  72.       maybe-note-nil-indexed-object
  73.       maybe-note-assembler-routine
  74.       maybe-note-single-storage-ref
  75.       maybe-note-associated-storage-ref
  76.       handle-break-args
  77.  
  78.       ;; taking over and printing...
  79.       print-notes-and-newline
  80.       print-current-address
  81.       print-bytes print-words
  82.       prin1-short
  83.       prin1-quoted-short
  84.       ))
  85.  
  86. ;;; This file implements a retargetable disassembler, that uses simple hooks
  87. ;;; in the compiler-backend's instruction definitions to learn about a
  88. ;;; machine's instruction set.
  89. ;;;
  90. ;;; Most of these hooks are actually called by the macros defined by
  91. ;;; assembler.lisp, and only add a few new keyword arguments.  These are
  92. ;;; described below.
  93. ;;;
  94. ;;; All of the symbols described here except for DEFINE-INSTRUCTION,
  95. ;;; DEFINE-FORMAT, and DEFINE-ARGUMENT-TYPE (which are from assembler.lisp)
  96. ;;; are interned in the DISASSEM package, which is not normally USEd by the
  97. ;;; instruction definition files, and so would have "DISASSEM:" prepended in
  98. ;;; normal use.
  99. ;;;
  100. ;;; The first thing that should be in the instruction definition file is a
  101. ;;; call to SET-DISASSEM-PARAMS, where the possible keyword arguments are:
  102. ;;;
  103. ;;;  :INSTRUCTION-ALIGNMENT    Minimum alignment of instructions, in bytes.
  104. ;;;  :ADDRESS-SIZE        Size of a machine address, in bits.
  105. ;;;  :OPCODE-COLUMN-WIDTH    Width of the column used for printing the opcode
  106. ;;;                  portion of the instruction, or NIL.
  107. ;;;  :STORAGE-CLASS-SETS    A list of mappings (SET-NAME SC-NAME*), which
  108. ;;;                defines which generic storage class [SET-NAME]
  109. ;;;                the compilers SCs map into.
  110. ;;;
  111. ;;; The assembler definition macros should be in the following order:
  112. ;;;  DEFINE-ARGUMENT-TYPE, DEFINE-FORMAT, DEFINE-INSTRUCTION.
  113. ;;;
  114. ;;;
  115. ;;; *** New keyword arguments to DEFINE-ARGUMENT-TYPE:
  116. ;;;
  117. ;;;  :DISASSEM-PRINTER    Used to print an instruction field of this type.  Can
  118. ;;;            be a string, which is used as a format-string, with
  119. ;;;            the field's value as the only argument; a vector,
  120. ;;;            which is indexed using the field's value, and printed
  121. ;;;            using PRINC; T, meaning the field's value is printed
  122. ;;;            verbatim, or a function, which is called with three
  123. ;;;            arguments: the value, the output stream, and a
  124. ;;;            structure of type DISASSEM-STATE (which can be used
  125. ;;;            in various ways; see below).  Typically you should
  126. ;;;            make sure that whatever you print or supply to be
  127. ;;;            printed should respect *PRINT-CASE* (so, e.g., if you
  128. ;;;            use an array of register names, they should probably
  129. ;;;            be symbols instead of strings).
  130. ;;;  :SIGN-EXTEND    Non-NIL if this field should be sign-extended when it's
  131. ;;;            extracted from the instruction.
  132. ;;;  :USE-LABEL        Indicates that this field should have an address
  133. ;;;            label associated with it (which is then indicated at
  134. ;;;            the point of definition, and printed in place of the
  135. ;;;            field).  The value can be either T, in which case the
  136. ;;;            value of the field is used directly, or a function
  137. ;;;            taking two arguments, the value and a DISASSEM-STATE
  138. ;;;            structure, which should return the proper value to
  139. ;;;            use.
  140. ;;;
  141. ;;; The DISASSEM-STATE structure contains several fields of interest, notably
  142. ;;; DSTATE-CURPOS, which is the address of the current instruction being
  143. ;;; disassembled, and DSTATE-NEXTPOS, which is the address of the following
  144. ;;; instruction, and is needed for calling several auxilarly functions;
  145. ;;; notably at this point:
  146. ;;;
  147. ;;;  MAYBE-NOTE-ASSOCIATED-STORAGE-REF    Called with the an offset, a general
  148. ;;;            storage-class set (as defined in SET-DISASSEM-
  149. ;;;            PARAMS), a associated name, and a DISASSEM-STATE
  150. ;;;            object.  This checks to see if there's a source
  151. ;;;            variable mapped to OFFSET in the storage-class at
  152. ;;;            this point in the function, and if so, makes a note
  153. ;;;            saying that it's associated with the name you gave.
  154. ;;;
  155. ;;;
  156. ;;; *** New keyword arguments to DEFINE-FIXUP-TYPE:  The same three as for
  157. ;;;  DEFINE-ARGUMENT-TYPE. 
  158. ;;;
  159. ;;;
  160. ;;; *** New keyword arguments to DEFINE-FORMAT:
  161. ;;;  DEFINE-FORMAT takes two kinds of keyword arguments, one kind in the
  162. ;;;  initial header, after the name, and one kind in each field description.
  163. ;;;  To the header arguments is added:
  164. ;;;
  165. ;;;   :DISASSEM-PRINTER    Describes how instructions with this format should be
  166. ;;;            printed unless otherwise specified.  It can be either
  167. ;;;            a PRINTER-DESCRIPTION-LIST (described below), or a
  168. ;;;            function called four arguments: the actual bits
  169. ;;;            making it up, an instruction object, a stream and a
  170. ;;;            disassem-state [functions are not normally used].
  171. ;;;   :DISASSEM-CONTROL    A function that is called after the instruction has
  172. ;;;            been entirely printed, to allow for other side
  173. ;;;            effects; this function is called both during actual
  174. ;;;            disassembly, and during the initial pass when labels
  175. ;;;            are being calculated, at which time the STREAM
  176. ;;;            argument is NIL (so you can test it and avoid doing
  177. ;;;            things that only should happen during output).  Four
  178. ;;;            arguments are supplied: a DCHUNK, which contains the
  179. ;;;            actual bits of the instruction, and is usually just
  180. ;;;            passed to other functions, an INST is a structure
  181. ;;;            describing the instruction, a STREAM being currently
  182. ;;;            output to (or NIL if no output is being done), and a
  183. ;;;            DISASSEM-STATE.  Typically you use this function to
  184. ;;;            do things like printing handy margin notes (functions
  185. ;;;            to do which are described below).
  186. ;;;
  187. ;;;  To the field descriptions, only :DEFAULT-TYPE is added, which is
  188. ;;;   analogous to :ARGUMENT in DEFINE-INSTRUCTION, and simply supplies a
  189. ;;;   default for when the type is not specified in the instruction.  This is
  190. ;;;   important because sometimes the disassembler wants to print a constant
  191. ;;;   field as if it were a normal argument, and it needs to know the type to
  192. ;;;   do it properly.
  193. ;;;
  194. ;;; *** PRINTER-DESCRIPTION-LISTs:
  195. ;;;  These are a handy way to describe how an instruction should be printed,
  196. ;;;   and are used as a value of :DISASSEM-PRINTER in both DEFINE-FORMAT and
  197. ;;;   DEFINE-INSTRUCTION.  They are essentially a list, where each field is
  198. ;;;   one of:
  199. ;;;
  200. ;;;    A string        Printed as if by PRINC.
  201. ;;;    Something quoted    Printed as if by PRINC (so 'FOO respects
  202. ;;;            *PRINT-CASE*, which "FOO" does not).
  203. ;;;    :NAME        The name of the instruction is printed as if by
  204. ;;;            PRINC.
  205. ;;;    :TAB        The output is tabbed to the "argument column".
  206. ;;;    An atom        Must be the name of a field in this instruction, which
  207. ;;;            is printed according to its type.
  208. ;;;    A list        The contents contents are printed as a PDL.
  209. ;;;    (:CHOOSE choice1 choice2 ...)
  210. ;;;            The choices are gone through in turn, and the first
  211. ;;;            one in which *every* field reference (atom) is valid,
  212. ;;;            is printed as a PDL (this includes a choice of NIL, if a
  213. ;;;            default of nothing is wanted).  **All :CHOOSE operators
  214. ;;;            are executed before anything else is done, and no other
  215. ;;;                grammar is paid attention to while this is being
  216. ;;;             done.**  This means you can use a :CHOOSE clause in
  217. ;;;             places where it might seem ungrammatical, such as the
  218. ;;;             field in a test-clause (see below).
  219. ;;;    (:UNLESS test ...) If test is *not* true, then the rest of the list is
  220. ;;;            printed as a PDL.  See below for a description of tests
  221. ;;;    (:COND (test ...) ...)
  222. ;;;            Analogous to lisp COND, the first clause in which the
  223. ;;;            test is true has its cdr printed as a PDL.
  224. ;;;
  225. ;;;  Tests in :UNLESS and :COND are of either of the form:
  226. ;;;
  227. ;;;   T            Always true.
  228. ;;;   ([field] :AND test ...)    True if all the sub-tests are true.
  229. ;;;   ([field] :OR test ...)    True if any the sub-tests are true.
  230. ;;;   ([field] :NOT test)    True if the subtest isn't true.
  231. ;;;   ([field] :CONSTANT [value])
  232. ;;;            True if field is a constant, (and when value is
  233. ;;;            supplied, equal to it).
  234. ;;;   ([field] :SAME-AS [other-field])
  235. ;;;            True if field is constrained to be the same as some
  236. ;;;            other field (which must be other-field if it is
  237. ;;;            supplied).
  238. ;;;
  239. ;;;   Any test which ONLY has one element, may leave the parentheses out.
  240. ;;;
  241. ;;;   Note that the field in each is optional; if it is left out, then the
  242. ;;;    field from the nearest ancestor in the whole test which supplied one
  243. ;;;    is used.  If the top-most ancestor didn't, then the first atom in the
  244. ;;;    PDL accompanying the test in the :UNLESS or :COND is used-- this can
  245. ;;;    be very handy; a very commom idiom is:
  246. ;;;    (:unless (:constant 0) ", " field)
  247. ;;;    which prints ", <field contents>" unless the field is
  248. ;;;    specified to be zero.  An example of inheriting otherwise is:
  249. ;;;    (:unless (field1 :or (:constant 0) :same-as) field2)
  250. ;;;    which prints field2 if field1 is either specified to be a constant 0,
  251. ;;;    or to be the same as some other field (*not* just field2, though).
  252. ;;;
  253. ;;;   Also note that when a test does something based on whether a field is
  254. ;;;    constant or the same as another one, this is *only* a *static* test--
  255. ;;;    i.e., it means that an instruction which is specified with that
  256. ;;;    constraint [(:constant something) or (:same-as something)] will be
  257. ;;;    printed that way, but *not* cases where no instruction was defined
  258. ;;;    with such a constraint, but where it just happened to be true.  So if
  259. ;;;    you use a printer with a test of :constant 0 against a particular
  260. ;;;    field but never define an instruction flavor with :constant 0 as the
  261. ;;;    field description, then this test will never be true, even if
  262. ;;;    instructions get disassembled where that particular field *is* zero.
  263. ;;;
  264. ;;;  Two examples:
  265. ;;;
  266. ;;;   (:name :tab
  267. ;;;         (:unless (:same-as dest-reg) source-reg-1 ", ")
  268. ;;;         (:choose source-reg-2 immed) ", "
  269. ;;;         dest-reg))
  270. ;;;
  271. ;;;   (:name :tab
  272. ;;;         (:choose (source-reg-1 (:unless (:constant 0) "+" immed))
  273. ;;;              (:cond ((source-reg-2 :constant 0) source-reg-1)
  274. ;;;                     ((source-reg-1 :constant 0) source-reg-2)
  275. ;;;                       (t source-reg-1 "+" source-reg-2)))
  276. ;;;         (:unless (:constant 0) ", " dest-reg)))
  277. ;;;
  278. ;;;
  279. ;;; *** New keyword arguments to DEFINE-INSTRUCTION:
  280. ;;;  DEFINE-INSTRUCTION takes two kinds of keyword arguments, one kind in the
  281. ;;;  initial header, after the name, and one kind in each field description.
  282. ;;;  To the header arguments is added the same two as DEFINE-FORMAT, which,
  283. ;;;  if specified, override the default ones in this instruction's format.
  284. ;;;
  285. ;;;  To the individual field instructions are added:
  286. ;;;
  287. ;;;   :TYPE        Can be used to specify the type of this field when
  288. ;;;            it's not done by :ARGUMENT (e.g., when it's a
  289. ;;;            :CONSTANT).
  290. ;;;   :MASK        Takes an integer constant.  For use with an :ARGUMENT
  291. ;;;            field, specifies that only part of the field is
  292. ;;;            actually variable-- those bits that are 1 in the
  293. ;;;            mask.  The rest of the bits in the field are assumed
  294. ;;;            to be constant 0.  This is needed in certain cases to
  295. ;;;            make sure the instructions are unambiguous.
  296. ;;;
  297. ;;;
  298. ;;; *** Handy functions that can be called from :DISASSEM-PRINTER or
  299. ;;; :DISASSEM-CONTROL functions:
  300. ;;;
  301. ;;;  ARG-VALUE field-name dchunk inst
  302. ;;;            Returns the value of the given field in the
  303. ;;;            instruction.
  304. ;;;  NOTE note dstate    Records a note to be printed in the end-of-line
  305. ;;;            comments.  The note can be either a string, which is
  306. ;;;            printed by PRINC, or a function taking a stream
  307. ;;;            argument.
  308. ;;;  MAYBE-NOTE-NIL-INDEXED-SYMBOL-SLOT-REF integer-offset dstate
  309. ;;;            If the offset added to NIL points to a valid slot in
  310. ;;;            a symbol, a note describing this is added to the
  311. ;;;            end-of-line comments.
  312. ;;;  MAYBE-NOTE-CODE-CONSTANT integer-offset dstate
  313. ;;;            If the offset from the function's code-object refers
  314. ;;;            to a valid constant-slot, a note describing this is
  315. ;;;            added to the end-of-line comments.
  316. ;;;  MAYBE-NOTE-SINGLE-STORAGE-REF integer-offset gen-storage-class-name dstate
  317. ;;;            If there's a currently valid source-variable mapped
  318. ;;;            at this offset in the storage class specified, a note
  319. ;;;            to this effect is made for the end-of-line comments.
  320. ;;;
  321. ;;;
  322. ;;; *** How to SPECIALIZE an instruction:
  323. ;;;  Often it's desirable to have a different :DISASSEM-PRINTER only in
  324. ;;;  certain cases, e.g., when a certain register field refers to a certain
  325. ;;;  register.  One way you can do this is to define the instruction using a
  326. ;;;  normal printer, and use the SPECIALIZE macro to define modified versions
  327. ;;;  with different printer or control functions.  The syntax is like:
  328. ;;;
  329. ;;;   SPECIALIZE (inst-name [:DISASSEM-PRINTER printer]
  330. ;;;                [:DISASSEM-CONTROL control]
  331. ;;;                [:NAME new-name])
  332. ;;;          field-constraint*
  333. ;;;
  334. ;;;  The keyword args are just like for DEFINE-FORMAT and
  335. ;;;  DEFINE-INSTRUCTION.  The field-constraints are either just field names,
  336. ;;;  which mean that the specialize operation should only affect flavors of
  337. ;;;  the instruction that contain that field, or a field-name and a
  338. ;;;  constraint, like:
  339. ;;;
  340. ;;;   (field-name :CONSTANT const-val)
  341. ;;;  or
  342. ;;;   (field-name :SAME-AS other-field-name)
  343. ;;;
  344. ;;;  Any instruction flavor that has all the specified fields will either
  345. ;;;  have its printer, control, or name arguments modified, if all its
  346. ;;;  constraints match those in the specialize, or will have a clone made
  347. ;;;  with the field constraints tightened to match, and the clone will have
  348. ;;;  the new printer, control, or name arguments as specified.  Also see the
  349. ;;;  documentation for the SPECIALIZE macro.
  350. ;;;
  351. ;;;
  352. ;;; *** Debugging hints:
  353. ;;;  Because the disassembler caches the search tree that it builds to find
  354. ;;;  instructions, if you use the disassemble command and subsequently
  355. ;;;  re-execute some of the instruction-definitions with changes, it won't
  356. ;;;  see the changes until you flush the cache, like:
  357. ;;;  
  358. ;;;   (setf (disassem::params-inst-space
  359. ;;;         (c:backend-disassem-params c:*target-backend*))
  360. ;;;            nil)
  361. ;;;  
  362.  
  363. ;;; ----------------------------------------------------------------
  364.  
  365. (defun req () (error "Required argument missing"))
  366.  
  367. ;;; ----------------------------------------------------------------
  368.  
  369. (defvar *opcode-column-width* nil
  370.   "The width of the column in which instruction-names are printed.
  371.   NIL means use the default.  A value of zero gives the effect of not
  372.   aligning the arguments at all.")
  373. (defvar *note-column* 45
  374.   "The column in which end-of-line comments for notes are started.")
  375.  
  376. (defconstant default-opcode-column-width 6)
  377. (defconstant default-address-column-width 8)
  378. (defconstant label-column-width 7)
  379.  
  380. ;;; ----------------------------------------------------------------
  381.  
  382. (defstruct (params (:print-function %print-params))
  383.   (field-types (make-hash-table :test #'eq) :type hash-table)
  384.   (inst-formats (make-hash-table :test #'eq) :type hash-table)
  385.   (instructions (make-hash-table :test #'eq) :type hash-table)
  386.   (inst-space nil :type (or null inst-space))
  387.   (instruction-alignment vm:word-bytes :type fixnum)
  388.   (address-column-width default-address-column-width :type fixnum)
  389.   (opcode-column-width default-opcode-column-width :type (or null fixnum))
  390.   (storage-class-sets nil :type list)
  391.   (backend (req) :type c::backend)    ; for convenience
  392.   )
  393.  
  394. (defun %print-params (params stream level)
  395.   (declare (ignore level))
  396.   (format stream "#<Disassem parameters for ~a>"
  397.       (and (params-backend params)
  398.            (c:backend-name (params-backend params)))))
  399.  
  400. (defmacro set-disassem-params (&key instruction-alignment
  401.                     address-size
  402.                     (opcode-column-width nil opcode-column-width-p)
  403.                     storage-class-sets)
  404.   "Specify global disassembler params for C:*TARGET-BACKEND*.  Currently
  405.   includes:
  406.  
  407.   :INSTRUCTION-ALIGNMENT    Minimum alignment of instructions, in bits.
  408.   :ADDRESS-SIZE            Size of a machine address, in bits.
  409.   :OPCODE-COLUMN-WIDTH        Width of the column used for printing the opcode
  410.                   portion of the instruction, or NIL.
  411.   :STORAGE-CLASS-SETS        A list of mappings (SET-NAME SC-NAME*), which
  412.                 defines which generic storage class the
  413.                   compilers SCs map into."
  414.   `(let ((params
  415.       (or (c:backend-disassem-params c:*target-backend*)
  416.           (setf (c:backend-disassem-params c:*target-backend*)
  417.             (make-params :backend c:*target-backend*)))))
  418.      ,(when instruction-alignment
  419.     `(multiple-value-bind (bytes bits)
  420.          (truncate ,instruction-alignment vm:byte-bits)
  421.        (unless (zerop bits)
  422.          (error "Instruction alignment not a multiple of ~s" vm:byte-bits))
  423.        (setf (params-instruction-alignment params) bytes)))
  424.      ,(when address-size
  425.     `(setf (params-address-column-width params)
  426.            (* 2 ,address-size)))
  427.      ,(when opcode-column-width-p
  428.     `(setf (params-opcode-column-width params) ,opcode-column-width))
  429.      ,(when storage-class-sets
  430.     `(setf (params-storage-class-sets params) ,storage-class-sets))
  431.      (values)))
  432.  
  433. ;;; ----------------------------------------------------------------
  434. ;;; A Dchunk contains the bits we look at to decode an
  435. ;;; instruction.
  436. ;;; I tried to keep this abstract so that if using integers > the machine
  437. ;;; word size conses too much, it can be changed to use bit-vectors or
  438. ;;; something.
  439.  
  440. (defconstant dchunk-bits 32)
  441.  
  442. (deftype dchunk ()
  443.   `(unsigned-byte ,dchunk-bits))
  444.  
  445. (defconstant dchunk-zero 0)
  446. (defconstant dchunk-one #xFFFFFFFF)
  447.  
  448. (defmacro dchunk-copy (x)
  449.   x)
  450.  
  451. (defun dchunk-or (to from)
  452.   (declare (type dchunk to from))
  453.   (the dchunk (logior to from)))
  454. (defun dchunk-and (to from)
  455.   (declare (type dchunk to from))
  456.   (the dchunk (logand to from)))
  457. (defun dchunk-clear (to from)
  458.   (declare (type dchunk to from))
  459.   (the dchunk (logandc2 to from)))
  460. (defun dchunk-not (from)
  461.   (declare (type dchunk from))
  462.   (the dchunk (lognot from)))
  463.  
  464. (defmacro dchunk-andf (to from)
  465.   `(setf ,to (dchunk-and ,to ,from)))
  466. (defmacro dchunk-orf (to from)
  467.   `(setf ,to (dchunk-or ,to ,from)))
  468. (defmacro dchunk-clearf (to from)
  469.   `(setf ,to (dchunk-clear ,to ,from)))
  470.  
  471. (defun dchunk-make-mask (pos)
  472.   (the dchunk (mask-field pos -1)))
  473. (defun dchunk-make-field (pos value)
  474.   (the dchunk (dpb value pos 0)))
  475.  
  476. (defmacro make-dchunk (value)
  477.   value)
  478.  
  479. (defun sap-ref-dchunk (sap byte-offset byte-order)
  480.   (declare (type system:system-area-pointer sap)
  481.        (type fixnum byte-offset))
  482.   (if (eq byte-order :big-endian)
  483.       (+ (ash (system:sap-ref-8 sap byte-offset) 24)
  484.      (ash (system:sap-ref-8 sap (+ 1 byte-offset)) 16)
  485.      (ash (system:sap-ref-8 sap (+ 2 byte-offset)) 8)
  486.      (system:sap-ref-8 sap (+ 3 byte-offset)))
  487.       (+ (system:sap-ref-8 sap byte-offset)
  488.      (ash (system:sap-ref-8 sap (+ 1 byte-offset)) 8)
  489.      (ash (system:sap-ref-8 sap (+ 2 byte-offset)) 16)
  490.      (ash (system:sap-ref-8 sap (+ 3 byte-offset)) 24))))
  491.  
  492. (defun correct-dchunk-bytespec-for-endianness (bs unit-bits byte-order)
  493.   (if (eq byte-order :big-endian)
  494.       (byte (byte-size bs) (+ (byte-position bs) (- dchunk-bits unit-bits)))
  495.       bs))
  496.  
  497. (defun dchunk-extract (from pos)
  498.   (declare (type dchunk from))
  499.   (ldb pos (the dchunk from)))
  500.  
  501. (defun dchunk-corrected-extract (from pos unit-bits byte-order)
  502.   (declare (type dchunk from))
  503.   (if (eq byte-order :big-endian)
  504.       (ldb (byte (byte-size pos) (+ (byte-position pos) (- dchunk-bits unit-bits)))
  505.        (the dchunk from))
  506.       (ldb pos (the dchunk from))))
  507.  
  508. (defmacro dchunk-insertf (place pos value)
  509.   `(setf ,place (the dchunk (dpb ,value ,pos (the dchunk,place)))))
  510.  
  511. (defun dchunk= (x y)
  512.   (declare (type dchunk x y))
  513.   (= x y))
  514. (defmacro dchunk-zerop (x)
  515.   `(dchunk= ,x dchunk-zero))
  516.  
  517. (defun dchunk-strict-superset-p (sup sub)
  518.   (and (zerop (logandc2 sub sup))
  519.        (not (zerop (logandc2 sup sub)))))
  520.  
  521. (defun dchunk-count-bits (x)
  522.   (declare (type dchunk x))
  523.   (logcount x))
  524.  
  525. ;;; ----------------------------------------------------------------
  526.  
  527. (defun sign-extend (int size)
  528.   (declare (type integer int)
  529.        (fixnum size))
  530.   (if (logbitp (1- size) int)
  531.       (dpb int (byte size 0) -1)
  532.       int))
  533.  
  534. (defun aligned-p (num size)
  535.   "Returns non-NIL if NUM is aligned on a SIZE byte boundary."
  536.   (declare (type integer num)
  537.        (type fixnum size))
  538.   (zerop (logand (1- size) num)))
  539.  
  540. (defun align (num size)
  541.   "Return NUM aligned *upward* to a SIZE byte boundary."
  542.   (declare (type integer num)
  543.        (type fixnum size))
  544.   (logandc1 (1- size) (+ (1- size) num)))
  545.  
  546. ;;; ----------------------------------------------------------------
  547.  
  548. (defun integer-typespec-p (thing)
  549.   (and (or (consp thing) (symbolp thing))
  550.        (subtypep thing 'integer)))
  551.  
  552. (defun unsigned-typespec-p (thing)
  553.   (and (or (consp thing) (symbolp thing))
  554.        (subtypep thing '(integer 0))))
  555.  
  556. (defun signed-typespec-p (thing)
  557.   (and (or (consp thing) (symbolp thing))
  558.        (subtypep thing 'integer)
  559.        (not (subtypep thing '(integer 0)))))
  560.  
  561. ;;; ----------------------------------------------------------------
  562.  
  563. (defun self-evaluating-p (x)
  564.   (typecase x
  565.     (null t)
  566.     (keyword t)
  567.     (symbol (eq x t))
  568.     (cons nil)
  569.     (t t)))
  570.  
  571. ;;; ----------------------------------------------------------------
  572.  
  573. (defstruct (field-type (:conc-name ftype-))
  574.   (name nil :type symbol)
  575.   ;; if printer is T or NIL, the number is printed, if a vector, the Nth
  576.   ;; value is printed,  if a string, format is called with it and the value,
  577.   ;; and if a function, it is called with the value, the stream, and a dstate
  578.   (printer nil :type (or (member nil t) vector string function))
  579.   (sign-extend nil :type (member nil t))
  580.   (use-label nil :type (or (member t nil) function))
  581.   )
  582.  
  583. (defstruct (field-instance (:conc-name finst-))
  584.   (field (req) :type inst-format-field)
  585.   (type (req) :type (or field-type cons symbol))
  586.   (same-as nil :type (or null inst-format-field))
  587.   (inverse-function nil :type (or null function))
  588.   )
  589.  
  590. (defun finst-name (fi)
  591.   (field-name (finst-field fi)))
  592.  
  593. (defun gen-field-type-decl-form (name options)
  594.   (destructuring-bind (&key disassem-printer disassem-use-label
  595.                 sign-extend &allow-other-keys)
  596.       options
  597.     `(setf (gethash ',name
  598.             (params-field-types
  599.              (c:backend-disassem-params c:*target-backend*)))
  600.        (make-field-type :name ',name
  601.                 :printer ,disassem-printer
  602.                 :sign-extend ,sign-extend
  603.                 :use-label ,disassem-use-label))))
  604.  
  605. (defun parse-field-type (name field-types)
  606.   (or (gethash name field-types)
  607.       (if (integer-typespec-p name)
  608.       name
  609.       (error "Field-type ~s not defined, and not a subtype of integer"
  610.          name))))
  611.  
  612. ;;; ----------------------------------------------------------------
  613.  
  614. (defstruct (inst-format-field (:conc-name field-))
  615.   (name nil :type symbol)
  616.   (pos (byte 0 0))            ; :type bytespec
  617.   (default nil :type (or null integer))
  618.   (default-type nil :type (or null symbol cons field-type))
  619.   (inverse-function nil :type (or null function)))
  620.  
  621. (defstruct (inst-format (:conc-name format-)
  622.             (:print-function %print-inst-format))
  623.   (name nil :type symbol)
  624.   (length 0 :type fixnum)
  625.   (fields nil :type list)    ; of inst-format-field
  626.   (printer nil)
  627.   (control nil)
  628.   (ungrokable nil :type (member nil t))    ;
  629.   )
  630.  
  631. (defun %print-inst-format (format stream level)
  632.   (declare (ignore level) (stream stream))
  633.   (format stream "#<Instruction-format ~s ~:a>"
  634.       (format-name format)
  635.       (mapcar #'field-name (format-fields format))))
  636.  
  637. (defun gen-format-fields-maker-form (field-specs bits)
  638.   (let ((ungrokable nil))
  639.     (values
  640.      `(list
  641.        ,@(mapcar
  642.       #'(lambda (fspec)
  643.           (destructuring-bind (name pos
  644.                     &key default
  645.                     default-type
  646.                     function
  647.                     inverse-function
  648.                     &allow-other-keys)
  649.           fspec
  650.         (when (and (not (null function)) (null inverse-function))
  651.           (setf ungrokable t))
  652.         `(make-inst-format-field
  653.           :name ',name
  654.           :pos (correct-dchunk-bytespec-for-endianness
  655.             ,pos
  656.             ,bits
  657.             (c:backend-byte-order c:*target-backend*))
  658.           :default ,default
  659.           :default-type
  660.             (parse-field-type ',default-type
  661.                       (params-field-types
  662.                        (c:backend-disassem-params
  663.                     c:*target-backend*)))
  664.           :inverse-function
  665.             ,(and inverse-function
  666.               `#',inverse-function))))
  667.           field-specs))
  668.      ungrokable)))
  669.  
  670. (defun gen-inst-format-decl-form (name bits field-specs options)
  671.   "Return a form that declares an instruction format for the disassembler
  672.   referenced by C:*TARGET-BACKEND*.  Fields are:
  673.  
  674.   NAME        Name of instruction format
  675.   BITS        Length of format, in bits
  676.   FIELD-SPECS    A list of descriptions of the fields in this format.  Each
  677.           entry is a list of:
  678.               (NAME POS &KEY DEFAULT DEFAULT-TYPE FUNCTION INVERSE-FUNCTION)
  679.           Where POS is a byte-spec specifying the fields position
  680.           within the instruction, DEFAULT is an integer specifying the
  681.           value of the field when not otherwise specified, DEFAULT-TYPE
  682.           is the type of the field in this case, FUNCTION is not used,
  683.           but when present, means the field is unusable by the
  684.           disassembler except when INVERSE-FUNCTION is specified, which
  685.           is a mapping from the bits in a field to the correct value.
  686.   OPTIONS        Keyword options.  Currently includes:
  687.           :DISASSEM-PRINTER     Default printer for this format.
  688.           :DISASSEM-CONTROL    Default control for this format."
  689.   (multiple-value-bind (fields-form ungrokable)
  690.       (gen-format-fields-maker-form field-specs bits)
  691.     `(setf (gethash ',name
  692.             (params-inst-formats
  693.              (c:backend-disassem-params c:*target-backend*)))
  694.        (make-inst-format :name ',name
  695.                  :length (truncate ,bits vm:byte-bits)
  696.                  :fields ,fields-form
  697.                  :printer ,(getf options :disassem-printer)
  698.                  :control ,(getf options :disassem-control)
  699.                  :ungrokable ,ungrokable))))
  700.  
  701. ;;; ----------------------------------------------------------------
  702.  
  703. (defstruct (inst (:print-function %print-inst))
  704.   (name nil :type (or symbol string))
  705.  
  706.   (mask dchunk-zero :type dchunk)    ; bits in the inst that are constant
  707.   (id dchunk-zero :type dchunk)        ; value of those constant bits
  708.  
  709.   (format nil :type (or null inst-format))
  710.  
  711.   (printer nil :type (or list function))
  712.   (printer-source nil :type (or list function))
  713.   (control nil :type (or null function))
  714.   (use-label nil :type (or null function))
  715.  
  716.   (specializers nil :type list)        ; of inst
  717.   (args nil :type list)            ; of field-instance (these are the
  718.                     ; non-constant fields)
  719.   )
  720.  
  721. (defun %print-inst (inst stream level)
  722.   (declare (ignore level) (stream stream))
  723.   (format stream "#<Instruction ~s ~:a {~s}>"
  724.       (inst-name inst)
  725.       (mapcar #'(lambda (field)
  726.               (cond ((inst-field-same-as-p inst field)
  727.                  (format nil "~a=~a"
  728.                      (field-name field)
  729.                      (field-name (inst-field-same-as inst field))))
  730.                 ((inst-field-const-p inst field)
  731.                  (format nil "~a=~d"
  732.                      (field-name field)
  733.                      (inst-field-const-value inst field)))
  734.                 (t
  735.                  (field-name field))))
  736.           (remove-if #'(lambda (field)
  737.                  (and (inst-field-const-p inst field)
  738.                       (null (inst-field-type inst field))))
  739.                  (format-fields (inst-format inst))))
  740.       (format-name (inst-format inst))))
  741.     
  742. (defun format-field-or-lose (name format)
  743.   (or (find name (format-fields format) :key #'field-name)
  744.       (error "Unknown field ~s in instruction format ~a"
  745.          name
  746.          (format-name format))))
  747.  
  748. (defun parse-inst-field (field-spec inst-format field-types)
  749.   (destructuring-bind (field-name
  750.                &key constant type same-as mask inverse-function
  751.                &allow-other-keys)
  752.       field-spec
  753.     (when (eq inverse-function #'identity)
  754.       (setf inverse-function nil))
  755.     (let* ((field (format-field-or-lose field-name inst-format))
  756.        (pos (field-pos field))
  757.        (same-as
  758.         (and same-as (format-field-or-lose same-as inst-format)))
  759.        (type
  760.         (if type
  761.         (parse-field-type type field-types)
  762.         (field-default-type field))))
  763.       (values
  764.        ;; constant mask
  765.        (cond (constant
  766.           (dchunk-make-mask pos))
  767.          (mask
  768.           (dchunk-clear (dchunk-make-mask pos)
  769.                 (dchunk-make-field pos mask)))
  770.          (t
  771.           dchunk-zero))
  772.        ;; constant id bits
  773.        (if constant
  774.        (dchunk-make-field pos constant)
  775.        dchunk-zero)
  776.        ;; field-instance
  777.        (if (and (eq type (field-default-type field)) ; using this instead of
  778.                              ; (null type) saves
  779.                              ; about 17kbytes
  780.         (null same-as)
  781.         (null inverse-function))
  782.        nil
  783.        (make-field-instance :field field
  784.                 :type type
  785.                 :same-as same-as
  786.                 :inverse-function inverse-function))))))
  787.  
  788. (defun propagate-same-as-types (inst)
  789.   "Propagate the types of specified fields to fields that are constrained to
  790.   be the same as them, but have no types of their own."
  791.   (let ((args (inst-args inst)))
  792.     (do ((change t))
  793.     ((not change))
  794.       (setf change nil)
  795.       (dolist (finst args)
  796.     (when (and (null (finst-type finst)) (finst-same-as finst))
  797.       (let* ((same-as-inst
  798.           (find (field-name (finst-same-as finst)) args
  799.             :key #'finst-name))
  800.          (type
  801.           (if same-as-inst
  802.               (finst-type same-as-inst)
  803.               (field-default-type (finst-same-as finst)))))
  804.         (unless (null type)
  805.           (setf change t)
  806.           (setf (finst-type finst) type))))))))
  807.  
  808. (defun parse-inst-fields (inst-field-specs inst-format field-types)
  809.   (let* ((mask dchunk-zero)
  810.      (id dchunk-zero)
  811.      (args nil))
  812.  
  813.     (dolist (field-spec inst-field-specs)
  814.       (multiple-value-bind (field-mask field-id field-inst)
  815.       (parse-inst-field field-spec inst-format field-types)
  816.     (dchunk-orf mask field-mask)
  817.     (dchunk-orf id field-id)
  818.     (when field-inst
  819.       (push field-inst args))))
  820.  
  821.     (dolist (field (format-fields inst-format))
  822.       (unless (find (field-name field) inst-field-specs :key #'car)
  823.     (let ((default (field-default field))
  824.           (default-type (field-default-type field)))
  825.       (unless (or default default-type)
  826.         (error "Field ~s in format ~s not supplied"
  827.            (field-name field)
  828.            (format-name inst-format)))
  829.       (cond (default
  830.          (dchunk-insertf id (field-pos field) default)
  831.          (dchunk-insertf mask (field-pos field) -1))))))
  832.  
  833.     (values mask id args)))
  834.  
  835. ;;; ----------------------------------------------------------------
  836.  
  837. (defun inst-field-type (inst field)
  838.   (let ((finst (find field (inst-args inst) :key #'finst-field)))
  839.     (if finst
  840.     (finst-type finst)
  841.     (field-default-type field))))
  842.  
  843. (defun inst-field-same-as (inst field)
  844.   (let ((finst (find field (inst-args inst) :key #'finst-field)))
  845.     (and finst
  846.      (finst-same-as finst))))
  847.  
  848. (defun inst-field-const-value (inst field)
  849.   "Returns the bit-pattern of FIELD in the instruction object INST."
  850.   (dchunk-extract (inst-id inst) (field-pos field)))
  851.  
  852. (defun inst-field-const-p (inst field &optional value)
  853.   "Returns non-NIL if FIELD in the instruction object INST is constrained to
  854.   be the constant bit-pattern VALUE.  If VALUE is NIL, then non-NIL is returned
  855.   if it's constrained to be any constant at all."
  856.   (and (let ((field-mask (dchunk-make-mask (field-pos field))))
  857.      ;; must be *all* ones
  858.      (dchunk= (dchunk-and (inst-mask inst) field-mask)
  859.           field-mask))
  860.        (or (null value)            ; any constant will do
  861.        (= value
  862.           (inst-field-const-value inst field)))))
  863.  
  864. (defun inst-nfield-const-p (inst name &optional value)
  865.   "Returns non-NIL if the field called NAME within the instruction object
  866.   INST is constrained to be the constant bit-pattern VALUE.  If VALUE is NIL,
  867.   then non-NIL is returned if it's constrained to be any constant at all."
  868.   (let ((field (find name (format-fields (inst-format inst)) :key #'field-name)))
  869.     (when (null field)
  870.       (error "Unknown field ~s in ~s" name inst))
  871.     (inst-field-const-p inst field value)))
  872.  
  873. (defun inst-field-same-as-p (inst field &optional other-field)
  874.   "Returns non-NIL if FIELD within the instruction object
  875.   INST is constrained to be the :SAME-AS as the field OTHER-FIELD.  If
  876.   OTHER-FIELD is NIL, then non-NIL is returned if it's constrained to be the
  877.   same as any other field."
  878.   (let ((finst (find field (inst-args inst) :key #'finst-field)))
  879.     (cond ((null finst)
  880.        nil)
  881.       ((not (null (finst-same-as finst)))
  882.        (or (null other-field)    ; just generically
  883.            (eq other-field (finst-same-as finst))))
  884.       (t
  885.        nil))))
  886.  
  887. (defun inst-nfield-same-as-p (inst name &optional other-field)
  888.   "Returns non-NIL if the field called NAME within the instruction object
  889.   INST is constrained to be the :SAME-AS as the field named OTHER-FIELD.  If
  890.   OTHER-FIELD is NIL, then non-NIL is returned if it's constrained to be the
  891.   same as any other field."
  892.   (let ((finst (find name (inst-args inst) :key #'finst-name)))
  893.     (cond ((null finst)
  894.        (unless (find name
  895.              (format-fields (inst-format inst))
  896.              :Key #'field-name)
  897.          (error "Unknown field ~s in ~s" name inst))
  898.        nil)
  899.       ((not (null (finst-same-as finst)))
  900.        (or (null other-field)    ; just generically
  901.            (eq other-field (field-name (finst-same-as finst)))))
  902.       (t
  903.        nil))))
  904.  
  905. ;;; ----------------------------------------------------------------
  906. ;;; All this stuff here filters a printer specification-list, removing
  907. ;;; anything that's irrelevant, and reducing it to a single flat list of
  908. ;;; strings (to be printed verbatim) and atoms (field-names).
  909.  
  910. (defun choose-in-printer-p (printer)
  911.   "Returns non-NIL if :CHOOSE occurs somewhere in PRINTER."
  912.   (if (atom printer)
  913.       (eq printer :choose)
  914.       (some #'choose-in-printer-p printer)))
  915.  
  916. (defun all-printer-fields-in-format-p (printer format)
  917.   (cond ((or (null printer) (keywordp printer) (eq printer t))
  918.      t)
  919.     ((symbolp printer)
  920.      (find printer (format-fields format) :key #'field-name))
  921.     ((listp printer)
  922.      (every #'(lambda (x) (all-printer-fields-in-format-p x format))
  923.         printer))
  924.     (t
  925.      t)))
  926.  
  927. (defun find-choice-in-format (choices format)
  928.   (dolist (choice choices
  929.           (error "No suitable choice for format ~s found in ~s"
  930.              format choices))
  931.     (when (choose-in-printer-p choice)
  932.       (setf choice (filter-printer-for-inst-format choice format)))
  933.     (when (all-printer-fields-in-format-p choice format)
  934.       (return choice))))
  935.  
  936. (defun filter-printer-for-inst-format (printer format)
  937.   "Returns a version of the disassembly-template PRINTER with any :CHOOSE
  938.   operators resolved properly for the instruction format FORMAT.  (:CHOOSE Sub*)
  939.   simply returns the first Sub in which every field reference refers to a field
  940.   within FORMAT."
  941.   (if (and (choose-in-printer-p printer) (listp printer))
  942.       (if (eq (car printer) :choose)
  943.       (find-choice-in-format (cdr printer) format)
  944.       (mapcar #'(lambda (sub)
  945.               (filter-printer-for-inst-format sub format))
  946.           printer))
  947.       printer))
  948.  
  949. (defun cons-maybe-cat (s cdr)
  950.   "Returns (CONS S CDR), but if both S and the car of CDR are strings, they
  951.   are concatenated."
  952.   (if (and (stringp s) (stringp (car cdr)))
  953.       (cons (concatenate 'string s (car cdr)) (cdr cdr))
  954.       (cons s cdr)))
  955.  
  956. (defun eval-test (subj test inst)
  957.   "Returns the result of the conditional TEST, with a default field-name of
  958.   SUBJ, in the instruction object INST."
  959.   (when (and (consp test) (symbolp (car test)) (not (keywordp (car test))))
  960.     (setf subj (car test)
  961.       test (cdr test)))
  962.   (and test
  963.        (let ((key (if (consp test) (car test) test))
  964.          (body (if (consp test) (cdr test) nil)))
  965.      (cond ((eq key :constant)
  966.         (inst-nfield-const-p inst subj (and body (car body))))
  967.            ((eq key :same-as)
  968.         (inst-nfield-same-as-p inst subj (and body (car body))))
  969.            ((eq key :or)
  970.         (some #'(lambda (sub) (eval-test subj sub inst)) body))
  971.            ((eq key :and)
  972.         (some #'(lambda (sub) (eval-test subj sub inst)) body))
  973.            ((eq key :not)
  974.         (not (eval-test subj body inst)))
  975.            ((and (consp key) (null body))
  976.         (eval-test subj key inst))
  977.            ((eq key t)
  978.         t)
  979.            (t
  980.         (error "Bogus test-form: ~s" test))))))
  981.  
  982. (defun find-first-symbol (tree)
  983.   "Returns the first non-keyword symbol in a depth-first search of TREE."
  984.   (cond ((null tree)
  985.      nil)
  986.     ((and (symbolp tree) (not (keywordp tree)))
  987.      tree)
  988.     ((atom tree)
  989.      nil)
  990.     (t
  991.      (or (find-first-symbol (car tree))
  992.          (find-first-symbol (cdr tree))))))
  993.  
  994. (defun flatten-printer (printer inst)
  995.   "Returns a flat version of the disassembly template PRINTER, resolving any
  996.   conditionals, and substituting the instruction name for :NAME, etc."
  997.   (labels ((flatten (printer accum)
  998.          (flet ((handle-test-clause (clause do-when-true-p)
  999.               (destructuring-bind (test &rest body)
  1000.               clause
  1001.             (let ((first-field (find-first-symbol body)))
  1002.               (if (eq (eval-test first-field test inst)
  1003.                   do-when-true-p)
  1004.                   (values (flatten body accum) t)
  1005.                   (values nil nil))))))
  1006.            (cond ((null printer)
  1007.               accum)
  1008.              ((eq printer :name)
  1009.               (cons `',(inst-name inst) accum))
  1010.              ((atom printer)
  1011.               (when (and (symbolp printer)
  1012.                  (not (keywordp printer)))
  1013.             (when (not
  1014.                    (find printer
  1015.                      (format-fields (inst-format inst))
  1016.                      :key #'field-name))
  1017.               (error "Unknown field name ~s in printer for ~s"
  1018.                  printer inst)))
  1019.               (cons-maybe-cat printer accum))
  1020.              ((eq (car printer) 'quote)
  1021.               (cons printer accum))
  1022.              ((eq (car printer) :unless)
  1023.               (multiple-value-bind (result ok)
  1024.               (handle-test-clause (cdr printer) nil)
  1025.             (if ok
  1026.                 result
  1027.                 accum)))
  1028.              ((eq (car printer) :cond)
  1029.               (dolist (clause (cdr printer)
  1030.                       (error "No test clause succeeds: ~s"
  1031.                          printer))
  1032.             (multiple-value-bind (result ok)
  1033.                 (handle-test-clause clause t)
  1034.               (when ok
  1035.                 (return result)))))
  1036.              ((eq (car printer) :or)
  1037.               (dolist (sub (cdr printer)
  1038.                    (error "No suitable result for ~s found in ~s"
  1039.                       inst printer))
  1040.             (cond ((null sub)
  1041.                    (return nil))
  1042.                   (t
  1043.                    (let ((result (flatten sub accum)))
  1044.                  (unless (null result)
  1045.                    (return result)))))))
  1046.              (t
  1047.               (flatten (car printer)
  1048.                    (flatten (cdr printer) accum)))))))
  1049.     (flatten printer nil)))
  1050.       
  1051. (defun filter-printer-for-inst (printer inst)
  1052.   "Takes a complicated conditionalized disassembly template PRINTER, and
  1053.   returns a simple version customized for the instruction object INST,
  1054.   containing only those things which PRINT-INST-USING can handle."
  1055.   (if (functionp printer)
  1056.       printer
  1057.       (flatten-printer (filter-printer-for-inst-format printer
  1058.                                (inst-format inst))
  1059.                inst)))
  1060.  
  1061. ;;; ----------------------------------------------------------------
  1062.  
  1063. (defun gen-field-spec-forms (field-specs)
  1064.   `(list ,@(mapcar #'(lambda (fspec)
  1065.                (destructuring-bind (name op arg
  1066.                          &key mask function
  1067.                          inverse-function type
  1068.                          &allow-other-keys)
  1069.                fspec
  1070.              `(list ',name
  1071.                 ,@(ecase op
  1072.                    (:constant
  1073.                     `(:constant ,arg))
  1074.                    (:argument
  1075.                     `(:type ',arg))
  1076.                    (:same-as
  1077.                     `(:same-as ',arg)))
  1078.                 ,@(and mask `(:mask ,mask))
  1079.                 ,@(and function `(:function #',function))
  1080.                 ,@(and type
  1081.                        (if (eq op :argument)
  1082.                        (error "Can't specifiy both ~s and ~s"
  1083.                           :argument :type)
  1084.                        `(:type ',type)))
  1085.                 ,@(and inverse-function
  1086.                        `(:inverse-function #',inverse-function))
  1087.                 )))
  1088.            field-specs)))
  1089.  
  1090. (defun slow-reject-inst-p (inst)
  1091.   "Reject this instruction (slow becuase we've already gone to the trouble
  1092.   of making it)."
  1093.   (or (zerop (inst-mask inst))        ; probably some sort of data
  1094.                     ; instruction, which we can't handle
  1095.       (format-ungrokable (inst-format inst))
  1096.       ;; we *ignore* an instruction if it has an unprintable type... this
  1097.       ;; is a *STUPID* way of making sure we only have the ones we want...
  1098.       (some #'(lambda (arg)
  1099.         (let ((type (finst-type arg)))
  1100.           (and (field-type-p type)
  1101.                (null (ftype-printer type))
  1102.                (null (ftype-use-label type)))))
  1103.         (inst-args inst))))
  1104.  
  1105. (defun create-inst (name format-name field-specs printer control use-label params)
  1106.   (declare (type symbol name format-name)
  1107.        (type list field-specs)
  1108.        (type (or null list function) printer)
  1109.        (type (or null function) control)
  1110.        (type (or (member nil t) function) use-label)
  1111.        (type params params))
  1112.   (let ((inst-format (gethash format-name (params-inst-formats params)))
  1113.     (field-types (params-field-types params)))
  1114.     (when (null inst-format)
  1115.       (error "Unknown instruction format ~s (for instruction ~s)"
  1116.          format-name name))
  1117.     (multiple-value-bind (mask id args)
  1118.     (parse-inst-fields field-specs inst-format field-types)
  1119.       (let* ((printer-source
  1120.           (or printer (format-printer inst-format)))
  1121.          (inst
  1122.           (make-inst :name name
  1123.              :mask mask
  1124.              :id id
  1125.              :format inst-format
  1126.              :args args
  1127.              :control (or control (format-control inst-format))
  1128.              :use-label use-label
  1129.              :printer-source printer-source
  1130.              )))
  1131.     (unless (slow-reject-inst-p inst)
  1132.       (propagate-same-as-types inst)
  1133.       (setf (inst-printer inst)
  1134.         (filter-printer-for-inst printer-source inst))
  1135.       inst)))))
  1136.         
  1137. ;;; Notice any instruction flavor-specifications that we obviously can't
  1138. ;;; handle, without going to the trouble of consing up an instruction object.
  1139. (defun fast-reject-inst-p (flavor)
  1140.   (some #'(lambda (field-spec)
  1141.         ;; we can only handle function-ized things if they can be inverted
  1142.         (and (getf (cdddr field-spec) :function)
  1143.          (not (getf (cdddr field-spec) :inverse-function))))
  1144.     (cdr flavor)))
  1145.  
  1146. (defun gen-inst-decl-form (inst-name inst-flavors options &optional augment)
  1147.   (destructuring-bind (&key disassem-printer disassem-control
  1148.                 disassem-use-label (disassemble t)
  1149.                &allow-other-keys)
  1150.       options
  1151.     (when disassemble
  1152.       (let ((flavor-forms
  1153.          (delete nil
  1154.              (mapcar #'(lambda (flavor)
  1155.                  (destructuring-bind (format-name
  1156.                               &rest inst-field-specs)
  1157.                      flavor
  1158.                    (unless (fast-reject-inst-p flavor)
  1159.                      `(let ((inst
  1160.                          (create-inst ',inst-name
  1161.                               ',format-name
  1162.                               ,(gen-field-spec-forms
  1163.                                 inst-field-specs)
  1164.                               printer
  1165.                               control
  1166.                               use-label
  1167.                               params)))
  1168.                     (when inst
  1169.                       (push inst insts))))))
  1170.                  inst-flavors))))
  1171.     (unless (null flavor-forms)
  1172.       `(let* ((params (c:backend-disassem-params c:*target-backend*))
  1173.           (printer ,disassem-printer)
  1174.           (control ,disassem-control)
  1175.           (use-label ,disassem-use-label)
  1176.           (insts nil))
  1177.          ,@flavor-forms
  1178.          ,(if augment
  1179.           `(dolist (flav insts)
  1180.              (push flav
  1181.                (gethash ',inst-name (params-instructions params))))
  1182.           `(setf (gethash ',inst-name (params-instructions params))
  1183.              insts))))))))
  1184.  
  1185. (defmacro augment-instruction ((name &rest options) &body forms)
  1186.   `(gen-inst-decl-form ,name ,forms ,options 'c:*target-backend* t))
  1187.  
  1188. ;;; ----------------------------------------------------------------
  1189. ;;; stuff for specializing instructions
  1190.  
  1191. (defun inst-matches-spec-p (inst spec)
  1192.   (or (atom spec)
  1193.       (destructuring-bind (field-name constraint operand)
  1194.       spec
  1195.     (ecase constraint
  1196.       (:constant
  1197.        (inst-nfield-const-p inst field-name operand))
  1198.       (:same-as
  1199.        (inst-nfield-same-as-p inst field-name operand))))))
  1200.  
  1201. (defun inst-compatible-with-specs-p (inst specs)
  1202.   "Returns non-NIL if the instruction object INST does not violate any
  1203.   constraints in SPECS, and contains all fields therein."
  1204.   (every #'(lambda (spec)
  1205.          (let ((field
  1206.             (find (if (atom spec) spec (car spec))
  1207.               (format-fields (inst-format inst))
  1208.               :key #'field-name)))
  1209.            (and field
  1210.             (or (atom spec)
  1211.             (inst-matches-spec-p inst spec)
  1212.             (and (not (inst-field-const-p inst field))
  1213.                  (not (inst-field-same-as-p inst field)))))))
  1214.     specs))
  1215.  
  1216. (defun inst-matches-specs-p (inst specs)
  1217.   (every #'(lambda (spec) (inst-matches-spec-p inst spec)) specs))
  1218.  
  1219. (defun non-matching-specs (inst specs)
  1220.   (remove-if #'(lambda (spec) (inst-matches-spec-p inst spec)) specs))
  1221.  
  1222. (defun field-in-spec-p (field specs)
  1223.   (let ((name (field-name field)))
  1224.     (some #'(lambda (spec)
  1225.           (if (atom spec)
  1226.           (eq name spec)
  1227.           (eq name (car spec))))
  1228.       specs)))
  1229.  
  1230. (defun field-matches-in-insts-p (field inst other-inst)
  1231.   "Returns non-NIL if FIELD is the same in both INST and OTHER-INST."
  1232.   (cond ((inst-field-const-p inst field)
  1233.      (inst-field-const-p other-inst
  1234.                  field
  1235.                  (inst-field-const-value inst field)))
  1236.     ((inst-field-same-as-p inst field)
  1237.      (inst-field-same-as-p other-inst
  1238.                    field
  1239.                    (finst-same-as
  1240.                 (find field (inst-args inst)
  1241.                       :key #'finst-field))))))
  1242.  
  1243. (defun inst-matches-except-for (inst other-inst exception-specs)
  1244.   "Returns non-NIL if the instruction object INST matches OTHER-INST in all
  1245.   fields except for those named in EXCEPTION-SPECS."
  1246.   (and (eq (inst-format inst) (inst-format other-inst))
  1247.        (every #'(lambda (field)
  1248.           (or (field-in-spec-p field exception-specs)
  1249.               (field-matches-in-insts-p field inst other-inst)))
  1250.           (format-fields (inst-format inst)))))
  1251.  
  1252. (defun transmogrify-inst (old-inst specs)
  1253.   "Return a new copy of the instruction object OLD-INST, but with the
  1254.   additional field constraints SPECS."
  1255.   (let ((new-inst (copy-inst old-inst))
  1256.     (format (inst-format old-inst)))
  1257.  
  1258.     (setf (inst-args new-inst) (copy-list (inst-args old-inst)))
  1259.  
  1260.     (dolist (spec specs)
  1261.       ;; these better be real specs...
  1262.       (destructuring-bind (field-name operator operand)
  1263.       spec
  1264.     (let ((field (format-field-or-lose field-name format)))
  1265.       (ecase operator
  1266.         (:constant 
  1267.          (let ((pos (field-pos field)))
  1268.            (dchunk-orf (inst-mask new-inst) (dchunk-make-mask pos))
  1269.            (dchunk-orf (inst-id new-inst) (dchunk-make-field pos operand))))
  1270.         (:same-as
  1271.          (let ((finst
  1272.             (find field-name (inst-args new-inst) :key #'finst-name)))
  1273.  
  1274.            (cond ((null finst)
  1275.               (setf finst
  1276.                 (make-field-instance
  1277.                  :field field
  1278.                  :type (field-default-type field))))
  1279.              (t
  1280.               ;; we have to copy it, since it's currently shared with
  1281.               ;; the old instruction
  1282.               (setf (inst-args new-inst)
  1283.                 (delete finst (inst-args new-inst)))
  1284.               (setf finst
  1285.                 (copy-field-instance finst))))
  1286.  
  1287.            (push finst (inst-args new-inst))
  1288.            (setf (finst-same-as finst)
  1289.              (format-field-or-lose operand format))))))))
  1290.  
  1291.     new-inst))
  1292.  
  1293. (defun apply-specializations (inst specializations)
  1294.   (destructuring-bind (&key (disassem-printer (inst-printer inst))
  1295.                 (disassem-control (inst-control inst))
  1296.                 (name (inst-name inst)))
  1297.       specializations
  1298.     (setf (inst-printer inst) disassem-printer
  1299.       (inst-control inst) disassem-control
  1300.       (inst-name inst) name)
  1301.  
  1302.     ;; after any changes
  1303.     (propagate-same-as-types inst)
  1304.     (setf (inst-printer inst)
  1305.       (filter-printer-for-inst (inst-printer-source inst) inst))
  1306.     inst))
  1307.  
  1308. (defun specialize-insts (inst-list specs specializations)
  1309.   (let* ((applicable-insts        ; set that have the specified fields
  1310.       (remove-if-not #'(lambda (inst) (inst-compatible-with-specs-p inst specs))
  1311.              inst-list))
  1312.      (exact-matches
  1313.       (remove-if-not #'(lambda (inst) (inst-matches-specs-p inst specs))
  1314.              applicable-insts))
  1315.      (non-exact-matches
  1316.       (set-difference applicable-insts exact-matches))
  1317.      (results
  1318.       (remove-if #'(lambda (inst) (inst-compatible-with-specs-p inst specs))
  1319.              inst-list)))
  1320.     (dolist (inst exact-matches)
  1321.       (push (apply-specializations inst specializations)
  1322.         results))
  1323.     (dolist (inst non-exact-matches)
  1324.       (let ((non-matching-specs (non-matching-specs inst specs)))
  1325.     (unless (or (some #'(lambda (prev-result)
  1326.                   (inst-matches-except-for inst prev-result nil))
  1327.               results)
  1328.             (some #'(lambda (other-inst)
  1329.                   (inst-matches-except-for inst
  1330.                                other-inst
  1331.                                non-matching-specs))
  1332.               results))
  1333.       (push (apply-specializations (transmogrify-inst inst
  1334.                               non-matching-specs)
  1335.                        specializations)
  1336.         results))
  1337.     (push inst results)))
  1338.     results))
  1339.  
  1340. (eval-when (compile load eval)
  1341.   (defun make-specs-form (specs)
  1342.     `(list ,@(mapcar #'(lambda (spec)
  1343.              (if (atom spec)
  1344.                  `',spec
  1345.                  `(list ',(car spec)
  1346.                     ',(cadr spec)
  1347.                     ,(if (eq (cadr spec) :constant)
  1348.                      (caddr spec)
  1349.                      `',(cadr spec)))))
  1350.              specs)))
  1351.  
  1352.   (defun make-specializations-form (specializations)
  1353.     `(list ,@specializations))
  1354.   )
  1355.  
  1356. (defmacro specialize ((inst-name &rest specializations) &rest specs)
  1357.   "SPECIALIZE (Name Specialization-keywords*) Spec*
  1358.   where Spec is either a Field-name, or
  1359.     (Field-name Constraint Value)
  1360.   Constraint is either :SAME-AS, in which case Value should be the name of
  1361.   another field, or :CONSTANT, in which case Value should be a constant
  1362.   integer.
  1363.  
  1364.   Modifies the all instruction flavors named INST-NAME (possibly creating
  1365.   new, more specific ones), according to SPECS, and applies SPECIALIZATIONS to
  1366.   the resulting instructions.
  1367.  
  1368.   Specialization-keywords, is one of :DISASSEM-PRINTER or :DISASSEM-CONTROL,
  1369.   which have the same meaning as for DEFINE-INSTRUCTION, or :NAME, which lets
  1370.   you change the name to the given symbol/string.
  1371.  
  1372.   Any instruction flavors that match all given field-constraints exactly will
  1373.   be simply modified.  Any that don't, and don't have any conflicting
  1374.   constraints, will have copies made with the constraints applied to the
  1375.   copies, and then the copies modified.  If the resulting copy would be the
  1376.   same as an existing instruction, then it is not made.
  1377.  
  1378.   Only instruction flavors that contain all the the specified fields are used."
  1379.   `(let ((params (c:backend-disassem-params c:*target-backend*)))
  1380.      (setf (gethash ',inst-name (params-instructions params))
  1381.        (specialize-insts (gethash ',inst-name (params-instructions params))
  1382.                  ,(make-specs-form specs)
  1383.                  ,(make-specializations-form specializations)))
  1384.      ',inst-name))
  1385.  
  1386. ;;; ----------------------------------------------------------------
  1387. ;;; an instruction space holds all known machine instructions in a form that
  1388. ;;; can be easily searched
  1389.  
  1390. (defstruct (inst-space (:conc-name ispace-) (:print-function %print-ispace))
  1391.   (valid-mask dchunk-zero :type dchunk)    ; applies to *children*
  1392.   (choices nil :type (or list vector))
  1393.   )
  1394.  
  1395. (defun %print-ispace (ispace stream level)
  1396.   (declare (ignore level))
  1397.   (format stream "#<Instruction-space {~x}>" (kernel:get-lisp-obj-address ispace)))
  1398.  
  1399. (defstruct (inst-space-choice (:conc-name ischoice-))
  1400.   (common-id dchunk-zero :type dchunk)    ; applies to *parent's* mask
  1401.   (subspace (req) :type (or inst-space inst))
  1402.   )
  1403.  
  1404. ;;; ----------------------------------------------------------------
  1405. ;;; searching for an instruction in instruction space
  1406.  
  1407. (declaim (inline inst-matches-p choose-inst-specialization))
  1408.  
  1409. (defun inst-matches-p (inst chunk)
  1410.   "Returns non-NIL if all constant-bits in INST match CHUNK."
  1411.   (declare (type inst inst)
  1412.        (type dchunk chunk))
  1413.   (dchunk= (dchunk-and (inst-mask inst) chunk) (inst-id inst)))
  1414.  
  1415. (defun choose-inst-specialization (inst chunk)
  1416.   "Given an instruction object, INST, and a bit-pattern, CHUNK, picks the
  1417.   most specific instruction on INST's specializer list who's constraints are
  1418.   met by CHUNK.  If none do, then INST is returned."
  1419.   (declare (type inst inst)
  1420.        (type dchunk chunk))
  1421.   (or (dolist (spec (inst-specializers inst) nil)
  1422.     (declare (type inst spec))
  1423.     (and (inst-matches-p spec chunk)
  1424.          (every #'(lambda (arg)
  1425.             (declare (type field-instance arg))
  1426.             (let ((same-as (finst-same-as arg)))
  1427.               (or (null same-as)
  1428.                   (= (dchunk-extract chunk
  1429.                          (field-pos (finst-field arg)))
  1430.                  (dchunk-extract chunk
  1431.                          (field-pos same-as))))))
  1432.             (inst-args spec))
  1433.          (return spec)))
  1434.       inst))
  1435.  
  1436. (defun find-inst (chunk inst-space)
  1437.   "Returns the instruction object within INST-SPACE corresponding to the
  1438.   bit-pattern CHUNK, or NIL if there isn't one."
  1439.   (declare (type dchunk chunk)
  1440.        (type (or null inst-space inst) inst-space))
  1441.   (cond ((null inst-space)
  1442.      nil)
  1443.     ((inst-p inst-space)
  1444.      (if (inst-matches-p inst-space chunk)
  1445.          (choose-inst-specialization inst-space chunk)
  1446.          nil))
  1447.     ((inst-space-p inst-space)
  1448.      (let* ((mask (ispace-valid-mask inst-space))
  1449.         (id (dchunk-and mask chunk))
  1450.         (choices (ispace-choices inst-space)))
  1451.        (declare (type dchunk id mask))
  1452.        (cond ((listp choices)
  1453.           (dolist (choice choices)
  1454.             (declare (type inst-space-choice choice))
  1455.             (when (dchunk= id (ischoice-common-id choice))
  1456.               (return (find-inst chunk (ischoice-subspace choice))))))
  1457.          (t            ; must be a vector
  1458.           (error "WOT?  NOT IMPLEMENTED")))))))
  1459.  
  1460. ;;; ----------------------------------------------------------------
  1461. ;;; building the instruction space
  1462.  
  1463. ;;; sort of a hack, assume int types are more general
  1464. (defun field-type-specializes-p (ft1 ft2)
  1465.   "Returns non-NIL if the field-type FT1 is more specific than FT2."
  1466.   (and (integer-typespec-p ft2)
  1467.        (not (integer-typespec-p ft1))))
  1468.  
  1469. (defun inst-specializes-p (special general)
  1470.   "Returns non-NIL if the instruction SPECIAL is a more specific version of
  1471.   GENERAL (i.e., the same instruction, but with more constraints)."
  1472.   (declare (type inst special general))
  1473.   (and (eq (inst-format special)
  1474.        (inst-format general))
  1475.        (let ((smask (inst-mask special))
  1476.          (gmask (inst-mask general)))
  1477.      (and (dchunk= (inst-id general)
  1478.                (dchunk-and (inst-id special) gmask))
  1479.           (or (dchunk-strict-superset-p smask gmask)
  1480.           (and (dchunk= smask gmask)
  1481.                (some #'(lambda (spec-finst)
  1482.                  (let ((field (finst-field spec-finst)))
  1483.                    (and (not (inst-field-const-p general field))
  1484.                     (not (inst-field-same-as-p general field))
  1485.                     (or (finst-same-as spec-finst)
  1486.                         (field-type-specializes-p (finst-type spec-finst)
  1487.                                       (inst-field-type general field))))))
  1488.                  (inst-args special))))))))
  1489.  
  1490. ;;; a bit arbitrary, but should work ok...
  1491. (defun specializer-rank (inst)
  1492.   "Returns an integer corresponding to the specifivity of the instruction INST."
  1493.   (declare (type inst inst))
  1494.   (+ (* (dchunk-count-bits (inst-mask inst)) 4)
  1495.      (count-if-not #'null (inst-args inst) :key #'finst-same-as)))
  1496.  
  1497. (defun order-specializers (insts)
  1498.   "Order the list of instructions INSTS with more specific (more constant
  1499.   bits, or same-as argument constains) ones first.  Returns the ordered list."
  1500.   (declare (type list insts))
  1501.   (sort insts
  1502.     #'(lambda (i1 i2)
  1503.         (> (specializer-rank i1) (specializer-rank i2)))))
  1504.  
  1505. (defun try-specializing (insts)
  1506.   "Given a list of instructions INSTS, Sees if one of these instructions is a
  1507.   more general form of all the others, in which case they are put into its
  1508.   specializers list, and it is returned.  Otherwise an error is signaled."
  1509.   (declare (type list insts))
  1510.   (let ((masters (copy-list insts)))
  1511.     (dolist (possible-master insts)
  1512.       (dolist (possible-specializer insts)
  1513.     (unless (or (eq possible-specializer possible-master)
  1514.             (inst-specializes-p possible-specializer possible-master))
  1515.       (setf masters (delete possible-master masters))
  1516.       (return)            ; exit the inner loop
  1517.       )))
  1518.     (cond ((null masters)
  1519.        (error "Instructions aren't related: ~s" insts))
  1520.       ((cdr masters)
  1521.        (error "Multiple specializing master: ~s" masters))
  1522.       (t
  1523.        (let ((master (car masters)))
  1524.          (setf (inst-specializers master)
  1525.            (order-specializers (remove master insts)))
  1526.          master)))))
  1527.  
  1528. (defun build-inst-space (insts &optional (initial-mask dchunk-one))
  1529.   "Returns an instruction-space object corresponding to the list of
  1530.   instructions INSTS.  If the optional parameter INITIAL-MASK is supplied, only
  1531.   bits it has set are used."
  1532.   ;; This is done by finding any set of bits that's common to
  1533.   ;; all instructions, building an instruction-space node that selects on those
  1534.   ;; bits, and recursively handle sets of instructions with a common value for
  1535.   ;; these bits (which, since there should be fewer instructions than in INSTS,
  1536.   ;; should have some additional set of bits to select on, etc).  If there
  1537.   ;; are no common bits, or all instructions have the same value within those
  1538.   ;; bits, TRY-SPECIALIZING is called, which handles the cases of many
  1539.   ;; variations on a single instruction.
  1540.   (declare (type list insts)
  1541.        (type dchunk initial-mask))
  1542.   (cond ((null insts)
  1543.      nil)
  1544.     ((null (cdr insts))
  1545.      (car insts))
  1546.     (t
  1547.      (let ((vmask (dchunk-copy initial-mask)))
  1548.        (dolist (inst insts)
  1549.          (dchunk-andf vmask (inst-mask inst)))
  1550.        (if (dchunk-zerop vmask)
  1551.            (try-specializing insts)
  1552.            (let ((buckets nil))
  1553.          (dolist (inst insts)
  1554.            (let* ((common-id (dchunk-and (inst-id inst) vmask))
  1555.               (bucket (assoc common-id buckets :test #'dchunk=)))
  1556.              (cond ((null bucket)
  1557.                 (push (list common-id inst) buckets))
  1558.                (t
  1559.                 (push inst (cdr bucket))))))
  1560.          (let ((submask (dchunk-clear initial-mask vmask)))
  1561.            (if (= (length buckets) 1)
  1562.                (try-specializing insts)
  1563.                (let ((choices
  1564.                   (mapcar #'(lambda (bucket)
  1565.                       (make-inst-space-choice
  1566.                        :subspace (build-inst-space
  1567.                               (cdr bucket)
  1568.                               submask)
  1569.                        :common-id (car bucket)))
  1570.                       buckets)))
  1571.              ;; note that we could instead build a vector of
  1572.              ;; choices (which could be indexed), but that's more
  1573.              ;; complicated, so we leave it off for now.
  1574.              (make-inst-space :valid-mask vmask
  1575.                       :choices choices))
  1576.                ))))))))
  1577.  
  1578. ;;; ----------------------------------------------------------------
  1579. ;;; a space printer for debugging purposes
  1580.  
  1581. (defun ind-inst-args (inst ind)
  1582.   (format t "~v,10t~{ ~s~} ~8,'0x ~8,'0x"
  1583.       ind
  1584.       (mapcar #'(lambda (fi)
  1585.               (let ((same-as (finst-same-as fi))
  1586.                 (type (finst-type fi))
  1587.                 (name (field-name (finst-field fi))))
  1588.             (cond (same-as
  1589.                    (list name '= (field-name same-as)))
  1590.                   ((inst-field-const-p inst (finst-field fi))
  1591.                    (list name '=
  1592.                      (inst-field-const-value inst
  1593.                                  (finst-field fi))))
  1594.                   ((field-type-p type)
  1595.                    (list name (ftype-name type)))
  1596.                   (t
  1597.                    (list name type)))))
  1598.           (reverse (inst-args inst)))
  1599.       (inst-mask inst)
  1600.       (inst-id inst)))
  1601.  
  1602. (defun print-inst-space (inst-space &optional (indent 1))
  1603.   "Prints a nicely formatted version of INST-SPACE."
  1604.   (cond ((null inst-space)
  1605.      nil)
  1606.     ((inst-p inst-space)
  1607.      (format t "~vt[~s" indent (inst-name inst-space))
  1608.      (ind-inst-args inst-space indent)
  1609.      (dolist (inst (inst-specializers inst-space))
  1610.        (format t "~%~vt:~s" indent (inst-name inst))
  1611.        (ind-inst-args inst indent))
  1612.      (write-char #\])
  1613.      (terpri))
  1614.     (t
  1615.      (format t "~vt---- ~8,'0x ----~%" indent (ispace-valid-mask inst-space))
  1616.      (map nil
  1617.           #'(lambda (choice)
  1618.           (format t "~vt~8,'0x ==>~%"
  1619.               (+ 2 indent)
  1620.               (ischoice-common-id choice))
  1621.           (print-inst-space (ischoice-subspace choice)
  1622.                     (+ 4 indent)))
  1623.           (ispace-choices inst-space)))))
  1624.  
  1625. ;;;; ----------------------------------------------------------------
  1626. ;;;; the actual disassembly part
  1627. ;;;; ----------------------------------------------------------------
  1628.  
  1629. ;;; ----------------------------------------------------------------
  1630. ;;; getting at the source code...
  1631.  
  1632. (defstruct (source-form-cache (:conc-name sfcache-))
  1633.   (debug-source nil :type (or null di:debug-source))
  1634.   (top-level-form-index -1 :type fixnum)
  1635.   (top-level-form nil :type list)
  1636.   (form-number-mapping-table nil :type (or null (vector list)))
  1637.   (last-location-retrieved nil :type (or null di:code-location))
  1638.   (last-form-retrieved -1 :type fixnum)
  1639.   )
  1640.  
  1641. (defun get-top-level-form (debug-source tlf-index)
  1642.   (let ((name (di:debug-source-name debug-source)))
  1643.     (ecase (di:debug-source-from debug-source)
  1644.       (:file
  1645.        (cond ((not (probe-file name))
  1646.           (warn "The source file ~s no longer seems to exist" name)
  1647.           nil)
  1648.          (t
  1649.           (let ((start-positions
  1650.              (di:debug-source-start-positions debug-source)))
  1651.         (cond ((null start-positions)
  1652.                (warn "No start positions map")
  1653.                nil)
  1654.               (t
  1655.                (let* ((local-tlf-index
  1656.                    (- tlf-index
  1657.                   (di:debug-source-root-number debug-source)))
  1658.                   (char-offset
  1659.                    (aref start-positions local-tlf-index)))
  1660.              (with-open-file (f name)
  1661.                (cond ((= (di:debug-source-created debug-source)
  1662.                      (file-write-date name))
  1663.                   (file-position f char-offset))
  1664.                  (t
  1665.                   (warn "Source file ~s has been modified; ~@
  1666.                      Using form offset instead of file index"
  1667.                     name)
  1668.                   (dotimes (i local-tlf-index) (read f))))
  1669.                (read f)
  1670.                ))))))))
  1671.       ((:lisp :stream)
  1672.        (aref name tlf-index)))))
  1673.  
  1674. (defun cache-valid (loc cache)
  1675.   (and cache
  1676.        (and (eq (di:code-location-debug-source loc)
  1677.         (sfcache-debug-source cache))
  1678.         (eq (di:code-location-top-level-form-offset loc)
  1679.         (sfcache-top-level-form-index cache)))))
  1680.  
  1681. (defun get-source-form (loc context &optional cache)
  1682.   (let* ((cache-valid (cache-valid loc cache))
  1683.      (tlf-index (di:code-location-top-level-form-offset loc))
  1684.      (form-number (di:code-location-form-number loc))
  1685.      (top-level-form
  1686.       (if cache-valid
  1687.           (sfcache-top-level-form cache)
  1688.           (get-top-level-form (di:code-location-debug-source loc)
  1689.                   tlf-index)))
  1690.      (mapping-table
  1691.       (if cache-valid
  1692.           (sfcache-form-number-mapping-table cache)
  1693.           (di:form-number-translations top-level-form tlf-index))))
  1694.     (when (and (not cache-valid) cache)
  1695.       (setf (sfcache-debug-source cache) (di:code-location-debug-source loc)
  1696.         (sfcache-top-level-form-index cache) tlf-index
  1697.         (sfcache-top-level-form cache) top-level-form
  1698.         (sfcache-form-number-mapping-table cache) mapping-table))
  1699.     (cond ((null top-level-form)
  1700.        nil)
  1701.       ((> form-number (length mapping-table))
  1702.        (warn "Bogus form-number in form!  The source file has probably ~@
  1703.           been changed too much to cope with")
  1704.        (when cache
  1705.          ;; disable future warnings
  1706.          (setf (sfcache-top-level-form cache) nil))
  1707.        nil)
  1708.       (t
  1709.        (when cache
  1710.          (setf (sfcache-last-location-retrieved cache) loc)
  1711.          (setf (sfcache-last-form-retrieved cache) form-number))
  1712.        (di:source-path-context top-level-form
  1713.                    (aref mapping-table form-number)
  1714.                    context)))))
  1715.  
  1716. (defun get-different-source-form (loc context &optional cache)
  1717.   (if (and (cache-valid loc cache)
  1718.        (or (= (di:code-location-form-number loc)
  1719.           (sfcache-last-form-retrieved cache))
  1720.            (and (sfcache-last-location-retrieved cache)
  1721.             (di:code-location= loc (sfcache-last-location-retrieved cache)))))
  1722.       (values nil nil)
  1723.       (values (get-source-form loc context cache) t)))
  1724.  
  1725. ;;; ----------------------------------------------------------------
  1726. ;;;
  1727.  
  1728. ;;; All state during disassembly.  We store some seemingly redundant
  1729. ;;; information so that we can allow garbage collect during disassembly and
  1730. ;;; not get tripped up by a code block being moved...
  1731. (defstruct (disassem-state (:conc-name dstate-)
  1732.                (:print-function %print-dstate))
  1733.   (curpos 0 :type integer)        ; address of current instruction
  1734.   (nextpos 0 :type integer)        ; address of next instruction
  1735.  
  1736.   (code nil :type (or null kernel:code-component))
  1737.   (real-code-insts-addr 0 :type integer) ; address of instructions area (only
  1738.                     ; used for avoiding gc effects) in
  1739.                     ; the code object
  1740.   (code-insts-addr 0 :type integer)    ; the instruction area fixed at
  1741.                     ; creation time (doesn't change with
  1742.                     ; a gc)
  1743.   (code-insts-offset 0 :type fixnum)    ; offset of instruction area from the
  1744.                     ; start of the code object
  1745.  
  1746.   (segment-start 0 :type integer)    ; start of our instruction segment
  1747.   (segment-sap (req) :type system:system-area-pointer)
  1748.                     ; a sap pointing to our segment--
  1749.                     ; NOTE: this *may* be different from
  1750.                     ; segment-start!
  1751.   (segment-length 0 :type fixnum)    ; length thereof
  1752.  
  1753.   (alignment vm:word-bytes :type fixnum) ; what to align to in most cases
  1754.   (byte-order :little-endian
  1755.           :type (member :big-endian :little-endian))
  1756.  
  1757.   (properties nil :type list)        ; for user code to hang stuff off of
  1758.  
  1759.   (addr-print-len nil :type        ; used for prettifying printing
  1760.           (or null fixnum))
  1761.   (argument-column 0 :type fixnum)
  1762.   (output-state :beginning        ; to make output look nicer
  1763.           :type (member :beginning
  1764.                 :block-boundary
  1765.                 nil))
  1766.  
  1767.   (labels nil :type list)        ; alist of (address . label-number)
  1768.   (fun-header-addresses nil :type list)    ; list of byte-offsets from code
  1769.   (hooks nil :type list)        ; alist of (address . function)
  1770.  
  1771.   (label-hash (make-hash-table)        ; same info in a different form
  1772.           :type hash-table)
  1773.  
  1774.   ;; versions of the above being run through
  1775.   (cur-labels nil :type list)        ; alist of (address . label-number)
  1776.   (cur-fun-header-addresses nil :type list) ; list of byte-offsets from code
  1777.   (cur-hooks nil :type list)        ; alist of (address . function)
  1778.  
  1779.   (notes nil :type list)        ; for the current location
  1780.  
  1781.   (storage-info nil            ; info about source variables
  1782.         :type (or null storage-info))
  1783.   (current-valid-locations nil        ; currently active source variables
  1784.                :type (or null (vector bit)))
  1785.  
  1786.   (params (req) :type params)        ; a handy pointer ...
  1787.   )
  1788.  
  1789. (defun %print-dstate (dstate stream level)
  1790.   (declare (ignore level))
  1791.   (format stream "#<Disassembly state at #x~x in #x~x[~d]~@[ in ~s~]>"
  1792.       (dstate-curpos dstate)
  1793.       (dstate-segment-start dstate)
  1794.       (dstate-segment-length dstate)
  1795.       (dstate-code dstate)))
  1796.  
  1797. (defmacro dstate-get-prop (dstate name)
  1798.   "Get the value of the property called NAME in DSTATE.  Also setf'able."
  1799.   `(getf (dstate-properties ,dstate) ,name))
  1800.  
  1801. ;;; ----------------------------------------------------------------
  1802.  
  1803. (defun arg-value (name chunk inst)
  1804.   "Given the NAME of a field in the instruction with bit-pattern CHUNK and
  1805.   corresponding to the instruction object INST, returns two values: the
  1806.   contents of the field, and the field's type (which is either the type-spec of
  1807.   a subtype of integer or a FIELD-TYPE).  Any sign-extension is done here.  An
  1808.   error is signaled if NAME doesn't correspond to any field in INST."
  1809.   (declare (type symbol name)
  1810.        (type dchunk chunk)
  1811.        (type inst inst))
  1812.   (let* ((finst (find name (inst-args inst) :key #'finst-name))
  1813.      (field
  1814.       (if finst
  1815.           (finst-field finst)
  1816.           (find name (format-fields (inst-format inst))
  1817.             :key #'field-name)))) 
  1818.  
  1819.     (when (null field)
  1820.       ;; must be a constant?
  1821.       (error "Unknown field ~a in ~s" name inst))
  1822.  
  1823.     ;; ok we know about it
  1824.     (let ((type
  1825.        (if finst
  1826.            (finst-type finst)
  1827.            (field-default-type field)))
  1828.       (ifunc (and finst (finst-inverse-function finst)))
  1829.       (value (dchunk-extract chunk (field-pos field))))
  1830.  
  1831.       (when (null type)
  1832.     (error "Field ~s in ~s is of unknown type" name inst))
  1833.  
  1834.       (when (if (field-type-p type)
  1835.         (ftype-sign-extend type)
  1836.         (signed-typespec-p type))
  1837.     (setf value (sign-extend value (byte-size (field-pos field)))))
  1838.  
  1839.       (when ifunc
  1840.     (setf value (funcall ifunc value)))
  1841.  
  1842.       (values value type))))
  1843.  
  1844. ;; sort of ugly, but it tries to handle everything
  1845. (defun print-field (name chunk inst stream dstate)
  1846.   "Write the contents field called NAME of the instruction with bit-pattern
  1847.   CHUNK and corresponding to the instruction object INST, to STREAM.  The
  1848.   format in which the contents are written is either specified by the type of
  1849.   the field within the instruction, or as simple integer (signed or non-signed
  1850.   specified by the instruction).  If the instruction tags it as a label and
  1851.   it's value (modified by the field's COMPUTE-LABEL function) corresponds to a
  1852.   label known by DSTATE, then that is used instead."
  1853.   (declare (type symbol name)
  1854.        (type dchunk chunk)
  1855.        (type inst inst)
  1856.        (type stream stream)
  1857.        (type disassem-state dstate))
  1858.   (multiple-value-bind (value type)
  1859.       (arg-value name chunk inst)
  1860.     (cond ((field-type-p type)
  1861.        (let ((printer (ftype-printer type))
  1862.          (use-label (ftype-use-label type)))
  1863.  
  1864.          (when use-label
  1865.            (unless (eq use-label t)
  1866.          (setf value (funcall use-label value dstate)))
  1867.            (let ((lookup-label
  1868.               (gethash value (dstate-label-hash dstate))))
  1869.          (when lookup-label
  1870.            (setf value lookup-label))))
  1871.  
  1872.          (cond ((or (null printer) (eq printer t))
  1873.             (if (and use-label (integerp value))
  1874.             (write value :base 16 :radix t :stream stream)
  1875.             (princ value stream)))
  1876.            ((stringp printer)
  1877.             (format stream printer value))
  1878.            ((vectorp printer)
  1879.             (princ (aref printer value) stream))
  1880.            (t
  1881.             (funcall printer value stream dstate)))))
  1882.       (t
  1883.        (princ value stream)))))
  1884.  
  1885. (defun print-inst-using (printer chunk inst stream dstate)
  1886.   "Print a disassembled version of the instruction with bit-pattern CHUNK,
  1887.   and corresponding to the instruction object INST, to STREAM, using PRINTER as
  1888.   a template.  PRINTER should be a list, where each element is either a string,
  1889.   whose contents are written verbatim, :NAME, which causes the instruction name
  1890.   to be written, :TAB, which causes the cursor to be moved to the
  1891.   argument-column of the output, (QUOTE symbol), which causes the symbol to be
  1892.   written, or a symbol, which causes the contents of the field of this name
  1893.   within the instruction to be written."
  1894.   (declare (type list printer)
  1895.        (type dchunk chunk)
  1896.        (type inst inst)
  1897.        (type stream stream)
  1898.        (type disassem-state dstate))
  1899.   (dolist (element printer)
  1900.     (etypecase element
  1901.       (string
  1902.        (write-string element stream))
  1903.       (symbol
  1904.        (if (eq element :tab)
  1905.        (format stream "~v,1t" (dstate-argument-column dstate))
  1906.        (print-field element chunk inst stream dstate)))
  1907.       (cons
  1908.        (if (eq (car element) 'quote)
  1909.        (princ (cadr element) stream)
  1910.        (error "Bogus element ~s to print-inst-using" element)))
  1911.       )))
  1912.  
  1913. (defun print-inst (chunk inst stream dstate)
  1914.   "Print a disassembled version of the instruction with bit-pattern CHUNK,
  1915.   and corresponding to the instruction object INST to STREAM.  DSTATE is a
  1916.   DISASSEM-STATE object."
  1917.   (declare (type dchunk chunk)
  1918.        (type inst inst)
  1919.        (type stream stream)
  1920.        (type disassem-state dstate))
  1921.   (let ((printer (inst-printer inst)))
  1922.     (unless printer
  1923.       (error "I don't know how to print ~s" inst))
  1924.     (etypecase printer
  1925.       (function
  1926.        (funcall printer chunk inst stream dstate))
  1927.       (list
  1928.        (print-inst-using printer chunk inst stream dstate)))))
  1929.  
  1930. ;;; ----------------------------------------------------------------
  1931.  
  1932. (defmacro to-bytes (num)
  1933.   "Converts a word-offset NUM to a byte-offset."
  1934.   `(ash ,num vm:word-shift))
  1935.  
  1936. (defmacro to-words (num)
  1937.   "Converts a byte-offset NUM to a word-offset."
  1938.   `(ash ,num (- vm:word-shift)))
  1939.  
  1940. ;;; Code object layout:
  1941. ;;;    header-word
  1942. ;;;    code-size (starting from first inst, in words)
  1943. ;;;    entry-points (points to first function header)
  1944. ;;;    debug-info
  1945. ;;;    trace-table-offset (starting from first inst, in bytes)
  1946. ;;;    constant1
  1947. ;;;    constant2
  1948. ;;;    ...
  1949. ;;;    <padding to dual-word boundry>
  1950. ;;;    start of instructions
  1951. ;;;    ...
  1952. ;;;    function-headers and lra's buried in here randomly
  1953. ;;;    ...
  1954. ;;;    start of trace-table
  1955. ;;;    <padding to dual-word boundry>
  1956. ;;;
  1957. ;;; Function header layout (dual word aligned):
  1958. ;;;    header-word
  1959. ;;;    self pointer
  1960. ;;;    next pointer (next function header)
  1961. ;;;    name
  1962. ;;;    arglist
  1963. ;;;    type
  1964. ;;;
  1965. ;;; LRA layout (dual word aligned):
  1966. ;;;    header-word
  1967.  
  1968. (defconstant lra-size (to-bytes 1))
  1969.  
  1970. (defmacro with-matching-addresses ((thing-var address alist-place) &body body)
  1971.   (let ((source-alist-var (gensym))
  1972.     (cell-var (gensym))
  1973.     (address-var (gensym)))
  1974.     `(let ((,source-alist-var ,alist-place)
  1975.        (,address-var ,address))
  1976.        (loop
  1977.      (unless (and ,source-alist-var (<= (caar ,source-alist-var) ,address-var))
  1978.        (return))
  1979.      (let ((,cell-var (pop ,source-alist-var)))
  1980.        (when (= (car ,cell-var) ,address-var)
  1981.          (let ((,thing-var (cdr ,cell-var)))
  1982.            ,@body))
  1983.        (setf ,alist-place ,source-alist-var))))))
  1984.  
  1985. ;;; ----------------------------------------------------------------
  1986. ;;; Routines to find things in the lisp environment.  Obviously highly
  1987. ;;; implementation specific!
  1988.  
  1989. (defconstant groked-symbol-slots
  1990.   (sort `((,vm:symbol-value-slot . lisp::symbol-value)
  1991.       (,vm:symbol-function-slot . lisp::symbol-function)
  1992.       (,vm:symbol-raw-function-addr-slot . lisp::symbol-raw-function-addr)
  1993.       (,vm:symbol-setf-function-slot . lisp::symbol-setf-function)
  1994.       (,vm:symbol-plist-slot . lisp::symbol-plist)
  1995.       (,vm:symbol-name-slot . lisp::symbol-name)
  1996.       (,vm:symbol-package-slot . lisp::symbol-package))
  1997.     #'<
  1998.     :key #'car)
  1999.   "An alist of (SYMBOL-SLOT-OFFSET . ACCESS-FUNCTION-NAME) for slots in a
  2000. symbol object that we know about.")
  2001.  
  2002. (defun grok-symbol-slot-ref (address)
  2003.   "Given ADDRESS, try and figure out if which slot of which symbol is being
  2004.   refered to.  Of course we can just give up, so it's not a big deal...
  2005.   Returns two values, the symbol and the name of the access function of the
  2006.   slot."
  2007.   (declare (type integer address))
  2008.   (if (not (aligned-p address vm:word-bytes))
  2009.       (values nil nil)
  2010.       (do ((slots-tail groked-symbol-slots (cdr slots-tail)))
  2011.       ((null slots-tail)
  2012.        (values nil nil))
  2013.     (let* ((field (car slots-tail))
  2014.            (slot-offset (to-bytes (car field)))
  2015.            (maybe-symbol-addr (- address slot-offset))
  2016.            (maybe-symbol (kernel:make-lisp-obj (+ maybe-symbol-addr vm:other-pointer-type))))
  2017.       (when (symbolp maybe-symbol)
  2018.         (return (values maybe-symbol (cdr field))))))))
  2019.  
  2020. (defconstant nil-addr (kernel:get-lisp-obj-address nil))
  2021.  
  2022. (defun grok-nil-indexed-symbol-slot-ref (byte-offset)
  2023.   "Given a BYTE-OFFSET from NIL, try and figure out if which slot of which
  2024.   symbol is being refered to.  Of course we can just give up, so it's not a big
  2025.   deal...  Returns two values, the symbol and the access function."
  2026.   (declare (type fixnum byte-offset))
  2027.   (grok-symbol-slot-ref (+ nil-addr byte-offset)))
  2028.  
  2029. (defun get-nil-indexed-object (byte-offset)
  2030.   "Returns the lisp object located BYTE-OFFSET from NIL."
  2031.   (declare (type fixnum byte-offset))
  2032.   (kernel:make-lisp-obj (+ nil-addr byte-offset)))
  2033.  
  2034. (defun get-code-constant (byte-offset dstate)
  2035.   "Returns two values; the lisp-object located at BYTE-OFFSET in the constant
  2036.   area of the code-object in DSTATE and T, or NIL and NIL if there is no
  2037.   code-object in DSTATE."
  2038.   (declare (type fixnum byte-offset)
  2039.        (type disassem-state dstate))
  2040.   (let ((code (dstate-code dstate)))
  2041.     (if code
  2042.     (values
  2043.      (kernel:code-header-ref code
  2044.                  (ash (+ byte-offset vm:other-pointer-type)
  2045.                       (- vm:word-shift)))
  2046.      t)
  2047.     (values
  2048.      nil
  2049.      nil))))
  2050.  
  2051. (defvar *assembler-routines-by-addr* nil)
  2052.  
  2053. (defun find-assembler-routine (address)
  2054.   "Returns the name of the primitive lisp assembler routine located at
  2055.   ADDRESS, or NIL if there isn't one."
  2056.   (declare (type integer address))
  2057.   (when (null *assembler-routines-by-addr*)
  2058.     (setf *assembler-routines-by-addr* (make-hash-table))
  2059.     (maphash #'(lambda (name address)
  2060.          (setf (gethash address *assembler-routines-by-addr*) name))
  2061.          lisp::*assembler-routines*))
  2062.   (gethash address *assembler-routines-by-addr*))
  2063.  
  2064. ;;; ----------------------------------------------------------------
  2065. ;;; function ops
  2066.  
  2067. (defun fun-self (fun)
  2068.   (declare (type compiled-function fun))
  2069.   (ext:truly-the compiled-function (system:%primitive function-self fun)))
  2070.  
  2071. (defun fun-code (fun)
  2072.   (declare (type compiled-function fun))
  2073.   (kernel:function-code-header (fun-self fun)))
  2074.  
  2075. (defun fun-next (fun)
  2076.   (declare (type compiled-function fun))
  2077.   (ext:truly-the compiled-function (system:%primitive function-next fun)))
  2078.  
  2079. (defun fun-address (function)
  2080.   (declare (type compiled-function function))
  2081.   (- (kernel:get-lisp-obj-address function)
  2082.      vm:function-pointer-type))
  2083.  
  2084. (defun fun-offset (function)
  2085.   (declare (type compiled-function function))
  2086.   (- (fun-address function)
  2087.      (system:sap-int (kernel:code-instructions (fun-code function)))))
  2088.  
  2089. ;;; ----------------------------------------------------------------
  2090. ;;; Operations on code-components (which hold the instructions for
  2091. ;;; one or more functions).
  2092.  
  2093. (defun code-inst-area-length (code-component)
  2094.   "Returns the length of the instruction area in CODE-COMPONENT."
  2095.   (declare (type kernel:code-component code-component))
  2096.   (kernel:code-header-ref code-component vm:code-trace-table-offset-slot))
  2097.  
  2098. (defun code-inst-area-address (code-component)
  2099.   "Returns the address of the instruction area in CODE-COMPONENT."
  2100.   (declare (type kernel:code-component code-component))
  2101.   (system:sap-int (kernel:code-instructions code-component)))
  2102.  
  2103. (defun code-first-function (code-component)
  2104.   "Returns the first function in CODE-COMPONENT."
  2105.   (declare (type kernel:code-component code-component))
  2106.   (kernel:code-header-ref code-component vm:code-trace-table-offset-slot))
  2107.  
  2108. (defun code-addr-offset (code-component addr)
  2109.   "Returns the offset from the beginning of CODE-COMPONENT of ADDR."
  2110.   (declare (type kernel:code-component code-component)
  2111.        (type integer addr))
  2112.   (- addr (logandc1 vm:lowtag-mask (kernel:get-lisp-obj-address code-component))))
  2113.  
  2114. (defun code-offs-address (code-component offs)
  2115.   "Returns the address of OFFS bytes from the beginning of CODE-COMPONENT."
  2116.   (declare (type kernel:code-component code-component)
  2117.        (type fixnum offs))
  2118.   (+ offs (logandc1 vm:lowtag-mask (kernel:get-lisp-obj-address code-component))))
  2119.  
  2120. ;;; ----------------------------------------------------------------
  2121.  
  2122. (defun dstate-code-insts-offs-address (dstate offs)
  2123.   (declare (type disassem-state dstate)
  2124.        (type fixnum offs))
  2125.   (+ (dstate-code-insts-addr dstate)
  2126.      offs))
  2127.  
  2128. (defun dstate-true-code-address (dstate address)
  2129.   "Translates a canonical address in DSTATE's code-component (e.g.,
  2130.   at the position when DSTATE was first created) to the actual
  2131.   address, which could be different if the code-component was moved
  2132.   by a GC."
  2133.   (declare (type disassem-state dstate)
  2134.        (type integer address))
  2135.   (+ (- address
  2136.     (dstate-code-insts-addr dstate))
  2137.      (dstate-real-code-insts-addr dstate)))
  2138.  
  2139. (defun dstate-code-addr-offset (dstate address)
  2140.   "Translates an address in DSTATE's code-component to the address
  2141.   it would be if no GCs had occurred since DSTATE was created."
  2142.   (declare (type disassem-state dstate)
  2143.        (type integer address))
  2144.   (+ (- address
  2145.     (dstate-code-insts-addr dstate))
  2146.      (dstate-code-insts-offset dstate)))
  2147.  
  2148. (defun make-sorted-fun-header-addr-list (code)
  2149.   "Returns a sorted list of the ADDRESSES of function-headers in the
  2150.   code-object CODE."
  2151.   (do ((fun (kernel:code-header-ref code vm:code-entry-points-slot)
  2152.         (fun-next fun))
  2153.        (fun-header-addrs nil))
  2154.       ((null fun)
  2155.        (sort fun-header-addrs #'<))
  2156.     (let ((fun-offset (kernel:get-closure-length fun)))
  2157.       ;; There is function header fun-offset words from the
  2158.       ;; code header.
  2159.       (push (code-offs-address code (to-bytes fun-offset))
  2160.         fun-header-addrs))))
  2161.  
  2162. (defun lra-p (chunk dstate)
  2163.   "Returns non-NIL if CHUNK is a valid LRA header in DSTATE."
  2164.   (declare (type dchunk chunk)
  2165.        (type disassem-state dstate))
  2166.   (and (aligned-p (dstate-curpos dstate) (* 2 vm:word-bytes))
  2167.        (let ((byte-offset
  2168.           (dstate-code-addr-offset dstate (dstate-curpos dstate))))
  2169.      (= (dchunk-corrected-extract chunk
  2170.                       (byte vm:word-bits 0)
  2171.                       vm:word-bits
  2172.                       (dstate-byte-order dstate))
  2173.         (logior (ash (to-words byte-offset) vm:type-bits)
  2174.             vm:return-pc-header-type)))))
  2175.  
  2176. (defun at-fun-header-p (dstate)
  2177.   "Returns non-NIL if DSTATE is currently pointing at a function header."
  2178.   (declare (type disassem-state dstate))
  2179.   (let ((header-addresses (dstate-cur-fun-header-addresses dstate)))
  2180.     (when header-addresses
  2181.       (let ((addr (dstate-curpos dstate)))
  2182.     (if (< (car header-addresses) addr)
  2183.         (do ()
  2184.         ((or (null header-addresses)
  2185.              (>= (car header-addresses) addr))
  2186.          (cond ((and header-addresses
  2187.                  (= addr (car header-addresses)))
  2188.             (setf (dstate-cur-fun-header-addresses dstate) (cdr header-addresses))
  2189.             t)
  2190.                (t
  2191.             (setf (dstate-cur-fun-header-addresses dstate) header-addresses)
  2192.             nil)))
  2193.           (pop header-addresses))
  2194.         (if (= addr (car header-addresses))
  2195.         (progn
  2196.           (setf (dstate-cur-fun-header-addresses dstate) (cdr header-addresses))
  2197.           t)
  2198.         nil))))))
  2199.  
  2200. (defun print-fun-header (stream dstate)
  2201.   "Print the function-header (entry-point) pseudo-instruction at the current
  2202.   location in DSTATE to STREAM."
  2203.   (declare (type stream stream)
  2204.        (type disassem-state dstate))
  2205.   (let* ((code (dstate-code dstate))
  2206.      (woffs
  2207.       (to-words
  2208.        (dstate-code-addr-offset dstate (dstate-curpos dstate))))
  2209.      (name
  2210.       (kernel:code-header-ref code (+ woffs vm:function-header-name-slot)))
  2211.      (args
  2212.       (kernel:code-header-ref code (+ woffs vm:function-header-arglist-slot)))
  2213.      (type
  2214.       (kernel:code-header-ref code (+ woffs vm:function-header-type-slot))))
  2215.     (format stream ".~a ~s~:a" 'entry name args)
  2216.     (note #'(lambda (stream)
  2217.           (format stream "~:s" type)) ; use format to print NIL as ()
  2218.       dstate)))
  2219.  
  2220. (defun check-for-moved-code (dstate)
  2221.   "If the code object in DSTATE has moved since we last checked, make the sap
  2222.   pointing to the start of the segment point to its corresponding location at
  2223.   the new address.  Isn't worth much unless called with GCing turned off."
  2224.   (declare (type disassem-state dstate))
  2225.   (let ((code (dstate-code dstate)))
  2226.     (when code
  2227.       (let ((old-code-insts-addr (dstate-real-code-insts-addr dstate))
  2228.         (new-code-insts-addr (code-inst-area-address code)))
  2229.     (when (/= old-code-insts-addr
  2230.           new-code-insts-addr)
  2231. #+nil      (format t "~&;;; CODE MOVED: #x~x --> #x~x~%"
  2232.           old-code-insts-addr
  2233.           new-code-insts-addr)
  2234.       (setf (dstate-segment-sap dstate)
  2235.         (system:int-sap
  2236.          (+ new-code-insts-addr
  2237.             (- (dstate-segment-start dstate)
  2238.                (dstate-code-insts-addr dstate)))))
  2239. #+nil      (format t "~&;;; SAP now points at #x~x (#~x+~d)~%"
  2240.           (system:sap-int (dstate-segment-sap dstate))
  2241.           new-code-insts-addr
  2242.           (- (dstate-segment-start dstate)
  2243.                (dstate-code-insts-addr dstate)))
  2244.       (setf (dstate-real-code-insts-addr dstate)
  2245.         new-code-insts-addr))))))
  2246.  
  2247. ;;; ----------------------------------------------------------------
  2248.  
  2249. (defun rewind-current-segment (dstate)
  2250.   (declare (type disassem-state dstate))
  2251.   (setf (dstate-curpos dstate) (dstate-segment-start dstate)
  2252.     (dstate-cur-fun-header-addresses dstate) (dstate-fun-header-addresses dstate)
  2253.     (dstate-cur-labels dstate) (dstate-labels dstate)))
  2254.  
  2255. (defun compute-labels (dstate)
  2256.   "Make an initial non-printing disassembly pass through DSTATE, noting any
  2257.   addresses that are referenced by instructions."
  2258.   ;; add labels at the beginning with a label-number of nil; we'll notice
  2259.   ;; later and fill them in (and sort them)
  2260.   (declare (type disassem-state dstate))
  2261.   (let ((byte-order (dstate-byte-order dstate))
  2262.     (ispace (get-inst-space (dstate-params dstate)))
  2263.     (labels (dstate-labels dstate)))
  2264.  
  2265.     (rewind-current-segment dstate)
  2266.  
  2267.     (loop
  2268.       (unless (aligned-p (dstate-curpos dstate) (dstate-alignment dstate))
  2269.     (setf (dstate-curpos dstate)
  2270.           (align (dstate-curpos dstate) (dstate-alignment dstate))))
  2271.  
  2272.       (let ((offs (- (dstate-curpos dstate) (dstate-segment-start dstate))))
  2273.     (when (>= offs (dstate-segment-length dstate))
  2274.       ;; done!
  2275.       (setf (dstate-labels dstate) labels)
  2276.       (setf (dstate-notes dstate) nil) ; just in case any got
  2277.                     ; left there by labeling
  2278.                     ; (they shouldn't but...)
  2279.       (return labels))
  2280.  
  2281.     (system:without-gcing
  2282.      (check-for-moved-code dstate)
  2283.  
  2284.      (cond ((at-fun-header-p dstate)
  2285.         (incf (dstate-curpos dstate)
  2286.               (to-bytes vm:function-header-code-offset)))
  2287.            (t
  2288.         (let* ((chunk
  2289.             (sap-ref-dchunk (dstate-segment-sap dstate)
  2290.                     offs
  2291.                     byte-order))
  2292.                (inst (find-inst chunk ispace)))
  2293.           (labels ((add-label (addr)
  2294.                  (unless (find addr labels :test #'= :key #'car)
  2295.                    (push (cons addr nil) labels)))
  2296.                (maybe-label-field (field type)
  2297.                  (when (field-type-p type)
  2298.                    (let ((use-label (ftype-use-label type)))
  2299.                  (when use-label
  2300.                    (let* ((pos (field-pos field))
  2301.                       (value (dchunk-extract chunk pos)))
  2302.                      (when (ftype-sign-extend type)
  2303.                        (setf value
  2304.                          (sign-extend value
  2305.                               (byte-size pos))))
  2306.  
  2307.                      (let ((addr
  2308.                         (if (eq use-label t)
  2309.                         value
  2310.                         (funcall use-label
  2311.                              value dstate))))
  2312.                        (add-label addr))))))))
  2313.             (cond ((lra-p chunk dstate)
  2314.                (incf (dstate-curpos dstate) lra-size))
  2315.               ((null inst)
  2316.                ;; let alignment fix it up
  2317.                (incf (dstate-curpos dstate)))
  2318.               ((inst-use-label inst)
  2319.                (add-label (funcall (inst-use-label inst)
  2320.                            chunk inst dstate)))
  2321.               (t
  2322.                (setf (dstate-nextpos dstate)
  2323.                  (+ (dstate-curpos dstate)
  2324.                     (format-length (inst-format inst))))
  2325.  
  2326.                (let ((inst-args (inst-args inst)))
  2327.                  ;; Look at possible labels refered to in the
  2328.                  ;; various instruction fields.
  2329.                  ;; We do it in this order (explicit args, then fields
  2330.                  ;; NOT in the arg list) instead of the obvious
  2331.                  ;; one (just all fields, looking in arg list
  2332.                  ;; for type overrides) for efficiency.
  2333.                  (dolist (finst inst-args)
  2334.                    (maybe-label-field (finst-field finst)
  2335.                           (finst-type finst)))
  2336.                  (dolist (field (format-fields (inst-format inst)))
  2337.                    (let ((type (field-default-type field)))
  2338.                  (when type
  2339.                    (unless (find field inst-args :key #'finst-field)
  2340.                      (maybe-label-field field type)))))
  2341.                  )
  2342.  
  2343.                (if (inst-control inst)
  2344.                    (funcall (inst-control inst)
  2345.                     chunk inst nil dstate))
  2346.  
  2347.                (setf (dstate-curpos dstate)
  2348.                  (dstate-nextpos dstate))
  2349.                )))))))))))
  2350.  
  2351. (defun number-labels (dstate)
  2352.   "If any labels in DSTATE have been added since the last call to this
  2353.   function, give them label-numbers, enter them in the hash-table, and make
  2354.   sure the label list is in sorted order."
  2355.   (let ((labels (dstate-labels dstate)))
  2356.     (when (and labels (null (cdar labels)))
  2357.       ;; at least one label left un-numbered
  2358.       (setf labels (sort labels #'< :key #'car))
  2359.       (let ((max -1)
  2360.         (label-hash (dstate-label-hash dstate)))
  2361.     (dolist (label labels)
  2362.       (when (not (null (cdr label)))
  2363.         (setf max (max max (cdr label)))))
  2364.     (dolist (label labels)
  2365.       (when (null (cdr label))
  2366.         (incf max)
  2367.         (setf (cdr label) max)
  2368.         (setf (gethash (car label) label-hash)
  2369.           (format nil "L~d" max)))))
  2370.       (setf (dstate-labels dstate) labels))))
  2371.  
  2372. ;;; ----------------------------------------------------------------
  2373.  
  2374. (defun get-inst-space (params)
  2375.   "Get the instruction-space from PARAMS, creating it if necessary."
  2376.   (declare (type params params))
  2377.   (let ((ispace (params-inst-space params)))
  2378.     (when (null ispace)
  2379.       (let ((insts nil))
  2380.     (maphash #'(lambda (name inst-flavs)
  2381.              (declare (ignore name))
  2382.              (dolist (flav inst-flavs)
  2383.                (push flav insts)))
  2384.          (params-instructions params))
  2385.     (setf ispace (build-inst-space insts)))
  2386.       (setf (params-inst-space params) ispace))
  2387.     ispace))  
  2388.  
  2389. ;;; ----------------------------------------------------------------
  2390. ;;; add global hooks
  2391.  
  2392. (defun add-hook (dstate addr hook)
  2393.   (let ((entry (cons addr hook)))
  2394.     (if (null (dstate-hooks dstate))
  2395.     (setf (dstate-hooks dstate) (list entry))
  2396.     (push entry (cdr (last (dstate-hooks dstate)))))))
  2397.  
  2398. (defun add-note-hook (dstate addr note)
  2399.   (add-hook dstate
  2400.         addr
  2401.         #'(lambda (stream)
  2402.         (when stream
  2403.           (note note dstate)))))
  2404.  
  2405. (defun add-comment-hook (dstate addr comment)
  2406.   (add-hook dstate
  2407.         addr
  2408.         #'(lambda (stream)
  2409.         (when stream
  2410.           (write-string ";;; " stream)
  2411.           (etypecase comment
  2412.             (string
  2413.              (write-string comment stream))
  2414.             (function
  2415.              (funcall comment stream)))
  2416.           (terpri stream)))))
  2417.  
  2418. ;;; ----------------------------------------------------------------
  2419.  
  2420. (defun set-address-printing-range (dstate from length)
  2421.   (setf (dstate-addr-print-len dstate)
  2422.     ;; 4 bits per hex digit
  2423.     (ceiling (integer-length (logxor from (+ from length))) 4)))
  2424.  
  2425. (defun print-current-address (stream dstate)
  2426.   "Print the current address in DSTATE to STREAM, plus any labels that
  2427.   correspond to it, and leave the cursor in the instruction column."
  2428.   (declare (type stream stream)
  2429.        (type disassem-state dstate))
  2430.   (let ((address (dstate-curpos dstate))
  2431.     (address-column-width
  2432.      (params-address-column-width (dstate-params dstate)))
  2433.     (plen (dstate-addr-print-len dstate)))
  2434.  
  2435.     (when (null plen)
  2436.       (setf plen address-column-width)
  2437.       (set-address-printing-range dstate
  2438.                   (dstate-segment-start dstate)
  2439.                   (dstate-segment-length dstate)))
  2440.     (when (eq (dstate-output-state dstate) :beginning)
  2441.       (setf plen address-column-width))
  2442.  
  2443.     (format stream "~&~v,0t~v,'0x:"
  2444.         (- address-column-width plen)
  2445.         plen
  2446.         (ldb (byte (* 4 plen) 0) address))
  2447.     (with-matching-addresses (label-number address (dstate-cur-labels dstate))
  2448.       (format stream " L~d:" label-number))
  2449.     (format stream "~v,0t" (+ address-column-width 1 label-column-width))
  2450.     ))
  2451.  
  2452. ;;; ----------------------------------------------------------------
  2453.  
  2454. (defmacro with-print-restrictions (&rest body)
  2455.   `(let ((*print-pretty* t)
  2456.      (*print-lines* 2)
  2457.      (*print-length* 4)
  2458.      (*print-level* 3))
  2459.      ,@body))
  2460.  
  2461. (defun print-notes-and-newline (stream dstate)
  2462.   "Print a newline to STREAM, inserting any pending notes in DSTATE as
  2463.   end-of-line comments.  If there is more than one note, a separate line
  2464.   will be used for each one."
  2465.   (declare (type stream stream)
  2466.        (type disassem-state dstate))
  2467.   (with-print-restrictions
  2468.     (dolist (note (dstate-notes dstate))
  2469.       (format stream "~vt; " *note-column*)
  2470.       (etypecase note
  2471.     (string
  2472.      (write-string note stream))
  2473.     (function
  2474.      (funcall note stream)))
  2475.       (terpri stream))
  2476.     (fresh-line stream)
  2477.     (setf (dstate-notes dstate) nil)))
  2478.  
  2479. (defun print-bytes (num stream dstate)
  2480.   "Disassemble NUM bytes to STREAM as simple `BYTE' instructions"
  2481.   (declare (type fixnum num)
  2482.        (type stream stream)
  2483.        (type disassem-state dstate))
  2484.   (format stream "~a~vt" 'BYTE (dstate-argument-column dstate))
  2485.   (let ((sap (dstate-segment-sap dstate))
  2486.     (start-offs (- (dstate-curpos dstate) (dstate-segment-start dstate))))
  2487.     (dotimes (offs num)
  2488.       (unless (zerop offs)
  2489.     (write-string ", " stream))
  2490.       (format stream "#x~2,'0x" (system:sap-ref-8 sap (+ offs start-offs))))))
  2491.  
  2492. (defun print-words (num stream dstate)
  2493.   "Disassemble NUM machine-words to STREAM as simple `WORD' instructions"
  2494.   (declare (type fixnum num)
  2495.        (type stream stream)
  2496.        (type disassem-state dstate))
  2497.   (format stream "~a~vt" 'WORD (dstate-argument-column dstate))
  2498.   (let ((sap (dstate-segment-sap dstate))
  2499.     (start-offs (- (dstate-curpos dstate) (dstate-segment-start dstate))))
  2500.     (dotimes (offs num)
  2501.       (unless (zerop offs)
  2502.     (write-string ", " stream))
  2503.       (format stream "#x~8,'0x"
  2504.           (system:sap-ref-32 sap (+ offs start-offs))))))
  2505.  
  2506. ;;; ----------------------------------------------------------------
  2507.  
  2508. (defun create-dstate (code params)
  2509.   "Make a disassembler-state object for the code-component CODE (which may
  2510.   also be NIL).  the call to this function should probably be done with GC
  2511.   disabled, but if CODE is non-NIL, then it's safe to turn GCing back on
  2512.   again after it returns."
  2513.   (declare (type (or null kernel:code-component) code)
  2514.        (type params params))
  2515.   (let ((sap
  2516.      ;; something safe; it will get changed later
  2517.      (if code
  2518.          (kernel:code-instructions code)
  2519.          (system:int-sap
  2520.           (- (kernel:get-lisp-obj-address nil) vm:list-pointer-type) )))
  2521.     (inst-area-addr (if code (code-inst-area-address code) 0)))
  2522.     (make-disassem-state :code code
  2523.              :segment-sap sap
  2524.              :segment-start 0
  2525.              :segment-length 0
  2526.              :params params
  2527.  
  2528.              :fun-header-addresses
  2529.                (and code
  2530.                 (make-sorted-fun-header-addr-list code))
  2531.              :real-code-insts-addr inst-area-addr
  2532.              :code-insts-addr inst-area-addr
  2533.              :code-insts-offset
  2534.                (if code (code-addr-offset code inst-area-addr) 0)
  2535.  
  2536.              :argument-column
  2537.                (+ (or *opcode-column-width*
  2538.                   (params-opcode-column-width params)
  2539.                   0)
  2540.                   (params-address-column-width params)
  2541.                   1
  2542.                   label-column-width)
  2543.              :alignment
  2544.                (params-instruction-alignment params)
  2545.              :byte-order
  2546.                (c:backend-byte-order (params-backend params))
  2547.              )))
  2548.  
  2549. (defun set-dstate-segment (dstate base length)
  2550.   "Make the current segment in DSTATE to start at BASE and be LENGTH long"
  2551.   (let ((sap
  2552.      (if (integerp base)
  2553.          (system:int-sap (dstate-true-code-address dstate base))
  2554.          base))
  2555.     (addr (if (integerp base) base (system:sap-int base))))
  2556.     (setf (dstate-segment-sap dstate) sap
  2557.       (dstate-segment-start dstate) addr
  2558.       (dstate-segment-length dstate) length)
  2559.     nil))
  2560.  
  2561. (defun disassemble-current-segment (dstate stream)
  2562.   "Disassemble the current memory segment in DSTATE to STREAM."
  2563.   (declare (type stream stream)
  2564.        (type disassem-state dstate)
  2565.        (type (member t nil) t))
  2566.   (let ((ispace (get-inst-space (dstate-params dstate))))
  2567.     (fresh-line stream)            ; otherwise, was tabbing funny on the
  2568.                     ; first line
  2569.  
  2570.     (number-labels dstate)
  2571.     (rewind-current-segment dstate)
  2572.  
  2573.     (loop
  2574.       (let (offs)
  2575.     (loop
  2576.       (let ((curpos (dstate-curpos dstate))
  2577.         (align (dstate-alignment dstate)))
  2578.         (setf offs (- curpos (dstate-segment-start dstate)))
  2579.         (when (>= offs (dstate-segment-length dstate))
  2580.           ;; done!
  2581.           (return-from disassemble-current-segment))
  2582.  
  2583.         (with-matching-addresses (hook curpos (dstate-cur-hooks dstate))
  2584.           (funcall hook stream))
  2585.  
  2586.         (print-current-address stream dstate)
  2587.  
  2588.         (when (aligned-p curpos align)
  2589.           ;; stop trying to align things
  2590.           (return))
  2591.  
  2592.         (format stream "~a~vt~d~%" '.align
  2593.             (dstate-argument-column dstate)
  2594.             align)
  2595.         (setf (dstate-curpos dstate) (align curpos align))
  2596.         ;; now go around and try again!
  2597.         ))
  2598.  
  2599.     (system:without-gcing
  2600.      (check-for-moved-code dstate)
  2601.  
  2602.      (cond ((at-fun-header-p dstate)
  2603.         (print-fun-header stream dstate)
  2604.         (incf (dstate-curpos dstate)
  2605.               (to-bytes vm:function-header-code-offset)))
  2606.            (t
  2607.         (let* ((chunk
  2608.             (sap-ref-dchunk (dstate-segment-sap dstate)
  2609.                     offs
  2610.                     (dstate-byte-order dstate)))
  2611.                (inst (find-inst chunk ispace)))
  2612.  
  2613.           (cond ((lra-p chunk dstate)
  2614.              (princ '.lra stream)
  2615.              (incf (dstate-curpos dstate) lra-size))
  2616.             ((null inst)
  2617.              (let ((alignment (dstate-alignment dstate)))
  2618.                (multiple-value-bind (words bytes)
  2619.                    (truncate alignment vm:word-bytes)
  2620.                  (when (> words 0)
  2621.                    (print-words words stream dstate))
  2622.                  (when (> bytes 0)
  2623.                    (print-bytes bytes stream dstate)))
  2624.                (incf (dstate-curpos dstate) alignment)))
  2625.             (t
  2626.              (setf (dstate-nextpos dstate)
  2627.                    (+ (dstate-curpos dstate)
  2628.                   (format-length (inst-format inst))))
  2629.  
  2630.              (print-inst chunk inst stream dstate)
  2631.              (when (inst-control inst)
  2632.                (funcall (inst-control inst)
  2633.                     chunk inst stream dstate))
  2634.  
  2635.              (setf (dstate-curpos dstate)
  2636.                    (dstate-nextpos dstate))))))))
  2637.  
  2638.     (print-notes-and-newline stream dstate)
  2639.     (setf (dstate-output-state dstate) nil)
  2640.     ))))
  2641.  
  2642. ;;; ----------------------------------------------------------------
  2643.  
  2644. ;;; just for fun
  2645. (defun print-fun-headers (function)
  2646.   (declare (type compiled-function function))
  2647.   (let* ((self (fun-self function))
  2648.      (code (kernel:function-code-header self)))
  2649.     (format t "Code-header ~s: size: ~s, trace-table-offset: ~s~%"
  2650.         code
  2651.         (kernel:code-header-ref code vm:code-code-size-slot)
  2652.         (kernel:code-header-ref code vm:code-trace-table-offset-slot))
  2653.     (do ((fun (kernel:code-header-ref code vm:code-entry-points-slot)
  2654.           (fun-next fun)))
  2655.     ((null fun))
  2656.       (let ((fun-offset (kernel:get-closure-length fun)))
  2657.     ;; There is function header fun-offset words from the
  2658.     ;; code header.
  2659.     (format t "Fun-header ~s at offset ~d: ~s~a => ~s~%"
  2660.         fun
  2661.         fun-offset
  2662.         (kernel:code-header-ref
  2663.          code (+ fun-offset vm:function-header-name-slot))
  2664.         (kernel:code-header-ref
  2665.          code (+ fun-offset vm:function-header-arglist-slot))
  2666.         (kernel:code-header-ref
  2667.          code (+ fun-offset vm:function-header-type-slot)))))))
  2668.  
  2669. ;;; ----------------------------------------------------------------
  2670. ;;; stuff to use debugging-info to augment the disassembly
  2671.  
  2672. (defun code-function-map (code)
  2673.   (declare (type kernel:code-component code))
  2674.   (di::get-debug-info-function-map
  2675.    (kernel:code-header-ref code vm:code-debug-info-slot)))
  2676.  
  2677. (defstruct storage-class
  2678.   (name nil :type symbol)
  2679.   (locations #() :type (vector (or list fixnum)))
  2680.   )
  2681.  
  2682. (defstruct storage-info
  2683.   (classes nil :type list)        ; alist of (name . sc-info)
  2684.   (debug-variables #() :type vector)
  2685.   )
  2686.  
  2687. (defun dstate-debug-variables (dstate)
  2688.   "Return the vector of debug-variables currently associated with DSTATE."
  2689.   (declare (type disassem-state dstate))
  2690.   (storage-info-debug-variables (dstate-storage-info dstate)))
  2691.  
  2692. (defun find-valid-storage-location (offset sc-name dstate)
  2693.   "Given the OFFSET of a location within the storage class SC-NAME (which is
  2694.   *not* the same as the compiler's SC), see if there's a current mapping to a
  2695.   source variable in DSTATE, and if so, return the offset of that variable in
  2696.   the current debug-variable vector."
  2697.   (declare (type fixnum offset)
  2698.        (type symbol sc-name)
  2699.        (type disassem-state dstate))
  2700.   (let* ((storage-info
  2701.       (dstate-storage-info dstate))
  2702.      (storage-class
  2703.       (and storage-info
  2704.            (cdr (assoc sc-name (storage-info-classes storage-info)))))
  2705.      (currently-valid
  2706.       (dstate-current-valid-locations dstate)))
  2707.     (and storage-class
  2708.      (not (null currently-valid))
  2709.      (let ((locations (storage-class-locations storage-class)))
  2710.        (and (< offset (length locations))
  2711.         (let ((used-by (aref locations offset)))
  2712.           (and used-by
  2713.                (let ((debug-var-num
  2714.                   (typecase used-by
  2715.                 (fixnum
  2716.                  (and (not
  2717.                        (zerop (bit currently-valid used-by)))
  2718.                       used-by))
  2719.                 (list
  2720.                  (some #'(lambda (num)
  2721.                        (and (not
  2722.                          (zerop
  2723.                           (bit currently-valid num)))
  2724.                         num))
  2725.                        used-by)))))
  2726.              (and debug-var-num
  2727.                   (progn
  2728.                 ;; Found a valid storage reference!
  2729.                 ;; can't use it again until it's revalidated...
  2730.                 (setf (bit (dstate-current-valid-locations dstate)
  2731.                        debug-var-num)
  2732.                       0)
  2733.                 debug-var-num))
  2734.              ))))))))
  2735.  
  2736. (defun copy-bit-vector (bvec)
  2737.   "This is stupid."
  2738.   (declare (type simple-bit-vector bvec))
  2739.   (let ((copy (bit-not bvec)))
  2740.     (bit-not copy copy)
  2741.     copy))
  2742.  
  2743. (defun grow-vector (vec new-len &optional initial-element)
  2744.   "Return a new vector which has the same contents as the old one VEC, plus
  2745.   new cells (for a total size of NEW-LEN).  The additional elements are
  2746.   initailized to INITIAL-ELEMENT."
  2747.   (declare (type vector vec)
  2748.        (type fixnum new-len)) 
  2749.   (let ((new
  2750.      (make-sequence `(vector ,(array-element-type vec) ,new-len)
  2751.             new-len
  2752.             :initial-element initial-element)))
  2753.     (dotimes (i (length vec))
  2754.       (setf (aref new i) (aref vec i)))
  2755.     new))
  2756.  
  2757. (defun storage-info-for-debug-function (debug-function dstate)
  2758.   "Returns a STORAGE-INFO struction describing the object-to-source
  2759.   variable mappings from DEBUG-FUNCTION."
  2760.   (declare (type di:debug-function debug-function)
  2761.        (type disassem-state dstate))
  2762.   (let ((compiler-sc-vec
  2763.      (c::backend-sc-numbers
  2764.       (params-backend (dstate-params dstate))))
  2765.     (sc-sets
  2766.      (params-storage-class-sets
  2767.       (dstate-params dstate)))
  2768.     (classes
  2769.      nil)
  2770.     (debug-variables
  2771.      (di::debug-function-debug-variables debug-function)))
  2772.     (dotimes (debug-var-offset (length debug-variables))
  2773.       (let ((debug-var (aref debug-variables debug-var-offset)))
  2774. #+nil    (format t ";;; At offset ~d: ~s~%" debug-var-offset debug-var)
  2775.     (let* ((compiler-sc-offset
  2776.         (di::compiled-debug-variable-sc-offset debug-var))
  2777.            (compiler-sc
  2778.         (aref compiler-sc-vec
  2779.               (c:sc-offset-scn compiler-sc-offset)))
  2780.            (compiler-sc-name
  2781.         (c:sc-name compiler-sc))
  2782.            (set-name
  2783.         (car (find compiler-sc-name sc-sets
  2784.                :test #'(lambda (name set)
  2785.                      (member name (cdr set)))))))
  2786. #+nil      (format t ";;; CSC: ~s, SET: ~s[~d]~%" compiler-sc-name set-name
  2787.           (c:sc-offset-offset compiler-sc-offset))
  2788.       (unless (null set-name)
  2789.         (let ((class (cdr (assoc set-name classes))))
  2790.           (when (null class)
  2791.         (setf class (make-storage-class :name set-name))
  2792.         (push `(,set-name . ,class) classes))
  2793.           (let* ((locations (storage-class-locations class))
  2794.              (length (length locations))
  2795.              (offset (c:sc-offset-offset compiler-sc-offset)))
  2796.         (when (>= offset length)
  2797.           (setf locations
  2798.             (grow-vector locations
  2799.                      (max (* 2 length)
  2800.                       (1+ offset))
  2801.                      nil)
  2802.             (storage-class-locations class)
  2803.             locations))
  2804.         (let ((already-there (aref locations offset)))
  2805.           (cond ((null already-there)
  2806.              (setf (aref locations offset) debug-var-offset))
  2807.             ((eql already-there debug-var-offset))
  2808.             (t
  2809.              (if (listp already-there)
  2810.                  (pushnew debug-var-offset (aref locations offset))
  2811.                  (setf (aref locations offset)
  2812.                    (list debug-var-offset already-there)))))
  2813.           )))))))
  2814.     (make-storage-info :classes classes
  2815.                :debug-variables debug-variables)))
  2816.  
  2817. (defun source-available-p (debug-function)
  2818.   (handler-case
  2819.       (di:do-debug-function-blocks (block debug-function)
  2820.     (declare (ignore block))
  2821.     (return t))
  2822.     (di:no-debug-blocks () nil)))
  2823.  
  2824. (defun print-block-boundary (stream dstate)
  2825.   (let ((os (dstate-output-state dstate)))
  2826.     (when (not (eq os :beginning))
  2827.       (when (not (eq os :block-boundary))
  2828.     (terpri stream))
  2829.       (setf (dstate-output-state dstate)
  2830.         :block-boundary))))
  2831.  
  2832. (defun source-tracking-hooks-for-debug-function (debug-function dstate &optional sfcache)
  2833.   "Return a set of hooks to track to track the source code from DEBUG-FUNCTION during
  2834.   disassembly.  SFCACHE can be either NIL or it can be a SOURCE-FORM-CACHE
  2835.   structure, in which case it is used to cache forms from files."
  2836.   (declare (type di:debug-function debug-function)
  2837.        (type (or null source-form-cache) sfcache)
  2838.        (type disassem-state dstate))
  2839.   (let ((last-block-pc -1)
  2840.     (hooks nil))
  2841.     (flet ((add-hook (pc fun)
  2842.          (push (cons (dstate-code-insts-offs-address dstate pc) fun)
  2843.            hooks)))
  2844.       (handler-case
  2845.       (di:do-debug-function-blocks (block debug-function)
  2846.         (let ((first-location-in-block-p t))
  2847.           (di:do-debug-block-locations (loc block)
  2848.         (let ((pc (di::compiled-code-location-pc loc)))
  2849.  
  2850.           ;; Put blank lines in at block boundaries
  2851.           (when (and first-location-in-block-p
  2852.                  (/= pc last-block-pc))
  2853.             (setf first-location-in-block-p nil)
  2854.             (add-hook pc #'(lambda (stream)
  2855.                      (print-block-boundary stream dstate)))
  2856.             (setf last-block-pc pc))
  2857.  
  2858.           ;; Print out corresponding source; this information is not all
  2859.           ;; that accurate, but it's better than nothing
  2860.           (unless (zerop (di:code-location-form-number loc))
  2861.             (multiple-value-bind (form new)
  2862.             (get-different-source-form loc 0 sfcache)
  2863.               (when new
  2864.              (let ((at-block-begin (= pc last-block-pc)))
  2865.                (add-hook pc
  2866.                      #'(lambda (stream)
  2867.                      (when stream
  2868.                        (unless at-block-begin
  2869.                          (terpri stream))
  2870.                        (format stream ";;; [~d] "
  2871.                            (di:code-location-form-number loc))
  2872.                        (prin1-short form stream)
  2873.                        (terpri stream)
  2874.                        (terpri stream))))))))
  2875.  
  2876.           ;; Keep track of variable live-ness as best we can
  2877.           (let ((live-set
  2878.              (copy-bit-vector
  2879.               (di::compiled-code-location-live-set loc))))
  2880.             (add-hook pc
  2881.                   #'(lambda (stream)
  2882.                   (declare (ignore stream))
  2883.                   (setf (dstate-current-valid-locations dstate)
  2884.                     live-set)
  2885.       #+nil                (note #'(lambda (stream)
  2886.                         (let ((*print-length* nil))
  2887.                           (format stream "Live set: ~s" live-set)))
  2888.                     dstate))))
  2889.           ))))
  2890.     (di:no-debug-blocks () nil)))
  2891.     (nreverse hooks)))
  2892.  
  2893. ;;; ----------------------------------------------------------------
  2894.  
  2895. (defstruct (segment (:conc-name seg-)
  2896.             (:print-function %print-segment))
  2897.   (start 0 :type integer)
  2898.   (length 0 :type fixnum)
  2899.   (debug-function nil :type (or null di:debug-function))
  2900.   (storage-info nil :type (or null storage-info))
  2901.   (hooks nil :type list)
  2902.   )
  2903.  
  2904. (defun %print-segment (seg stream level)
  2905.   (declare (ignore level))
  2906.   (format stream "#<Memory segment #x~x[~d]~@[ in ~s~]>"
  2907.       (seg-start seg)
  2908.       (seg-length seg)
  2909.       (and (seg-debug-function seg)
  2910.            (di:debug-function-name (seg-debug-function seg)))))
  2911.  
  2912. (defun create-segment (addr length debug-function dstate &optional sfcache)
  2913.   "Return a memory segment starting at ADDR and LENGTH bytes long, and using
  2914.   debugging information from DEBUG-FUNCTION unless it's NIL.  SFCACHE may be
  2915.   optionally supplied to improve access to the source-code."
  2916.   (let ((storage-info
  2917.      (and debug-function
  2918.           (storage-info-for-debug-function debug-function
  2919.                            dstate)))
  2920.     (hooks
  2921.      (and debug-function
  2922.           (source-tracking-hooks-for-debug-function debug-function
  2923.                             dstate
  2924.                             sfcache))))
  2925.     (when debug-function
  2926.       (let ((kind (di:debug-function-kind debug-function)))
  2927.     (flet ((anh (n)
  2928.              (push (cons addr
  2929.                  #'(lambda (stream)
  2930.                  (declare (ignore stream))
  2931.                  (note n dstate)))
  2932.                hooks)))
  2933.       (case kind
  2934.         (:external)
  2935.         ((nil)
  2936.          (anh "No-arg-parsing entry point"))
  2937.         (t
  2938.          (anh #'(lambda (stream)
  2939.               (format stream "~s entry point" kind))))))))
  2940.     (make-segment :start addr
  2941.           :length length
  2942.           :debug-function debug-function
  2943.           :storage-info storage-info
  2944.           :hooks hooks)))
  2945.  
  2946. (defun get-function-segments (function dstate)
  2947.   "Returns a list of the segments of memory containing machine code
  2948.   instructions for FUNCTION.  DSTATE is used to translate from instruction
  2949.   offsets to addresses."
  2950.   (declare (type compiled-function function))
  2951.   (let* ((code (fun-code function))
  2952.      (function-map (code-function-map code))
  2953.      (fname (kernel:%function-header-name function))
  2954.      (sfcache (make-source-form-cache)))
  2955.     (let ((first-block-seen-p nil)
  2956.       (nil-block-seen-p nil)
  2957.       (last-offset 0)
  2958.       (last-debug-function nil)
  2959.       (segments nil))
  2960.       (flet ((add-seg (offs len df)
  2961.            (when (> len 0)
  2962.          (push (create-segment
  2963.             (dstate-code-insts-offs-address dstate offs)
  2964.             len
  2965.             df
  2966.             dstate
  2967.             sfcache)
  2968.                segments))))
  2969.     (dotimes (fmap-index (length function-map))
  2970.       (let ((fmap-entry (aref function-map fmap-index)))
  2971.         (etypecase fmap-entry
  2972.           (integer
  2973.            (when first-block-seen-p
  2974.          (add-seg last-offset
  2975.               (- fmap-entry last-offset)
  2976.               last-debug-function)
  2977.          (setf last-debug-function nil))
  2978.            (setf last-offset fmap-entry))
  2979.           (c::compiled-debug-function
  2980.            (let ((name (c::compiled-debug-function-name fmap-entry))
  2981.              (kind (c::compiled-debug-function-kind fmap-entry)))
  2982. #+nil             (format t ";;; SAW ~s ~s ~s,~s ~d,~d~%"
  2983.                        name kind first-block-seen-p nil-block-seen-p
  2984.                        last-offset (c::compiled-debug-function-start-pc fmap-entry))
  2985.          (cond (#+nil (eq last-offset fun-offset)
  2986.                   (and (equal name fname) (not first-block-seen-p))
  2987.                   (setf first-block-seen-p t))
  2988.                ((eq kind :external)
  2989.             (when first-block-seen-p
  2990.               (return)))
  2991.                ((eq kind nil)
  2992.             (when nil-block-seen-p
  2993.               (return))
  2994.             (when first-block-seen-p
  2995.               (setf nil-block-seen-p t))))
  2996.          (setf last-debug-function
  2997.                (di::make-compiled-debug-function fmap-entry code))
  2998.          )))))
  2999.     (let ((max-offset (code-inst-area-length code)))
  3000.       (when (and first-block-seen-p last-debug-function)
  3001.         (add-seg last-offset
  3002.              (- max-offset last-offset)
  3003.              last-debug-function))
  3004.       (if (null segments)
  3005.           (let ((offs (fun-offset function)))
  3006.         (make-segment
  3007.          :start (dstate-code-insts-offs-address dstate offs)
  3008.          :length (- max-offset offs)))
  3009.           (nreverse segments)))))))
  3010.  
  3011. (defun get-code-segments (dstate &optional
  3012.                  (start (dstate-code-insts-addr dstate))
  3013.                  (length (code-inst-area-length (dstate-code dstate))))
  3014.   "Returns a list of the segments of memory containing machine code
  3015.   instructions for the code-component in DSTATE.  If START and/or LENGTH is
  3016.   supplied, only that part of the code-segment is used (but these are
  3017.   constrained to lie within the code-segment)."
  3018.   (declare (type disassem-state dstate)
  3019.        (type integer start)
  3020.        (type fixnum length))
  3021.   (let ((code-component (dstate-code dstate))
  3022.     (segments nil))
  3023.     (when code-component
  3024.       (let ((function-map (code-function-map code-component))
  3025.         (sfcache (make-source-form-cache)))
  3026.     (let ((last-offset 0)
  3027.           (last-debug-function nil))
  3028.       (flet ((add-seg (offs len df)
  3029.               (let* ((raw-addr (dstate-code-insts-offs-address dstate offs))
  3030.                  (addr (min (max start raw-addr) (+ start length)))
  3031.                  (len
  3032.                   (- (min (max start (+ raw-addr len)) (+ start length))
  3033.                      addr)))
  3034.                 (when (> len 0)
  3035.                   (push (create-segment addr len df dstate sfcache)
  3036.                     segments)))))
  3037.         (dotimes (fmap-index (length function-map))
  3038.           (let ((fmap-entry (aref function-map fmap-index)))
  3039.         (etypecase fmap-entry
  3040.           (integer
  3041.            (add-seg last-offset (- fmap-entry last-offset) last-debug-function)
  3042.            (setf last-debug-function nil)
  3043.            (setf last-offset fmap-entry))
  3044.           (c::compiled-debug-function
  3045.            (setf last-debug-function
  3046.              (di::make-compiled-debug-function fmap-entry code-component)))
  3047.           )))
  3048.         (when last-debug-function
  3049.           (add-seg last-offset
  3050.                (- (code-inst-area-length code-component) last-offset)
  3051.                last-debug-function))))))
  3052.     (if (null segments)
  3053.     (make-segment :start start :length length)
  3054.     (nreverse segments))))
  3055.  
  3056. ;;; ----------------------------------------------------------------
  3057.  
  3058. #+nil
  3059. (defun find-function-segment (fun)
  3060.   "Return the address of the instructions for function and its length.
  3061.   The length is computed using a heuristic, and so may not be accurate."
  3062.   (declare (type compiled-function fun))
  3063.   (let* ((code
  3064.       (fun-code fun))
  3065.      (fun-addr
  3066.       (- (kernel:get-lisp-obj-address fun)
  3067.          vm:function-pointer-type))
  3068.      (max-length
  3069.       (code-inst-area-length code))
  3070.      (upper-bound
  3071.       (+ (code-inst-area-address code) max-length)))
  3072.     (do ((some-fun (code-first-function code)
  3073.            (fun-next some-fun)))
  3074.     ((null some-fun)
  3075.      (values fun-addr (- upper-bound fun-addr)))
  3076.       (let ((some-addr (fun-address some-fun)))
  3077.     (when (and (> some-addr fun-addr)
  3078.            (< some-addr upper-bound))
  3079.       (setf upper-bound some-addr))))))
  3080.  
  3081. ;;; ----------------------------------------------------------------
  3082.  
  3083. (defun label-segments (seglist dstate)
  3084.   "Computes labels for all the memory segments in SEGLIST and adds them to
  3085.   DSTATE.  It's important to call this function with all the segments you're
  3086.   interested in, so it can find references from one to another."
  3087.   (declare (type list seglist)
  3088.        (type disassem-state dstate))
  3089.   (dolist (seg seglist)
  3090.     (set-dstate-segment dstate (seg-start seg) (seg-length seg))
  3091.     (compute-labels dstate)))
  3092.  
  3093. (defun disassemble-segment (segment stream dstate)
  3094.   "Disassemble the machine code instructions in SEGMENT to STREAM."
  3095.   (set-dstate-segment dstate (seg-start segment) (seg-length segment))
  3096.   (setf (dstate-storage-info dstate) (seg-storage-info segment))
  3097.   (setf (dstate-cur-hooks dstate)
  3098.     (sort (append (seg-hooks segment) (dstate-hooks dstate)) #'< :key #'car))
  3099.   (disassemble-current-segment dstate stream))
  3100.  
  3101. (defun disassemble-segments (segments stream dstate)
  3102.   "Disassemble the machine code instructions in each memory segment in
  3103.   SEGMENTS in turn to STREAM."
  3104.   (unless (null segments)
  3105.     (let ((first (car segments))
  3106.       (last (car (last segments))))
  3107.       (set-address-printing-range dstate
  3108.                   (seg-start first)
  3109.                   (- (+ (seg-start last) (seg-length last))
  3110.                      (seg-start first)))
  3111.       (setf (dstate-output-state dstate) :beginning)
  3112.       (unless (and (seg-debug-function first)
  3113.            (source-available-p (seg-debug-function first)))
  3114.     (format stream "~&;;; Not enough debugging information to find source code~%"))
  3115.       (dolist (seg segments)
  3116.     (disassemble-segment seg stream dstate)))))
  3117.  
  3118. ;;; ----------------------------------------------------------------
  3119. ;;; top-level functions
  3120.  
  3121. (defun disassemble-function (function &key (stream *standard-output*)
  3122.                       (use-labels t)
  3123.                       (backend c:*backend*))
  3124.   "Disassemble the machine code instructions for FUNCTION."
  3125.   (declare (type compiled-function function)
  3126.        (type stream stream)
  3127.        (type (member t nil) use-labels)
  3128.        (type c::backend backend))
  3129.   (let* ((dstate
  3130.       (system:without-gcing
  3131.        (create-dstate (fun-code function)
  3132.               (c:backend-disassem-params backend))))
  3133.      (segments (get-function-segments function dstate)))
  3134.     (when use-labels
  3135.       (label-segments segments dstate))
  3136.     (disassemble-segments segments stream dstate)))
  3137.  
  3138. (defun compile-function-lambda-expr (function)
  3139.   (declare (type function function))
  3140.   (multiple-value-bind
  3141.       (lambda closurep name)
  3142.       (function-lambda-expression function)
  3143.     (declare (ignore name))
  3144.     (when closurep
  3145.       (error "Cannot compile a lexical closure"))
  3146.     (compile nil lambda)))
  3147.  
  3148. (defun compiled-function-or-lose (thing &optional (name thing))
  3149.   (cond ((or (symbolp thing)
  3150.          (and (listp thing)
  3151.           (eq (car thing) 'lisp:setf)))
  3152.      (compiled-function-or-lose (fdefinition thing) thing))
  3153.     ((eval:interpreted-function-p thing)
  3154.      (compile-function-lambda-expr thing))
  3155.     ((functionp thing)
  3156.      thing)
  3157.     ((and (listp thing)
  3158.           (eq (car thing) 'lisp::lambda))
  3159.      (compile nil thing))
  3160.     (t
  3161.      (error "Can't make a compiled function from ~S" name))))
  3162.  
  3163. (defun disassemble (object &optional (stream *standard-output*)
  3164.                &key (use-labels t)
  3165.                (backend c:*backend*))
  3166.   "Disassemble the machine code associated with OBJECT, which can be a
  3167.   function, a lambda expression, or a symbol with a function definition.  If
  3168.   it is not already compiled, the compiler is called to produce something to
  3169.   disassemble.  If STREAM is T, *STANDARD-OUTPUT* is used (so you can 
  3170.   use the keywords without having to type it!)."
  3171.   (declare (type (or function symbol cons) object)
  3172.        (type (or (member t) stream) stream)
  3173.        (type (member t nil) use-labels)
  3174.        (type c::backend backend))
  3175.   (disassemble-function (fun-self    ; we can't detect closures, so
  3176.                     ; be careful
  3177.              (compiled-function-or-lose object))
  3178.             :stream (if (eq stream t)
  3179.                     *standard-output*
  3180.                     stream)
  3181.             :use-labels use-labels
  3182.             :backend backend))
  3183.  
  3184. (defun disassemble-memory (address length
  3185.                    &key (stream *standard-output*)
  3186.                    code-component
  3187.                    (use-labels t) (backend c:*backend*))
  3188.   "Disassembles the given area of memory starting at ADDRESS and LENGTH long.
  3189.   Note that if CODE-COMPONENT is NIL and this memory could move during a GC,
  3190.   you'd better disable it around the call to this function."
  3191.   (declare (type (or integer system:system-area-pointer) address)
  3192.        (type fixnum length)
  3193.        (type stream stream)
  3194.        (type (or null kernel:code-component) code-component)
  3195.        (type (member t nil) use-labels)
  3196.        (type c::backend backend))
  3197.   (let*    ((dstate
  3198.       (system:without-gcing
  3199.        (create-dstate code-component
  3200.               (c:backend-disassem-params backend))))
  3201.      (segments
  3202.       (if code-component
  3203.           (get-code-segments dstate address length)
  3204.           (list (make-segment :start address :length length)))))
  3205.     (when use-labels
  3206.       (label-segments segments dstate))
  3207.     (disassemble-segments segments stream dstate)))
  3208.  
  3209. (defun disassemble-code-component (code-component &key
  3210.                           (stream *standard-output*)
  3211.                           (use-labels t)
  3212.                           (backend c:*backend*))
  3213.   "Disassemble the machine code instructions associated with
  3214.   CODE-COMPONENT (this may include multiple entry points)."
  3215.   (declare (type (or null kernel:code-component compiled-function) code-component)
  3216.        (type stream stream)
  3217.        (type (member t nil) use-labels)
  3218.        (type c::backend backend))
  3219.   (let*    ((dstate
  3220.       (system:without-gcing
  3221.        (create-dstate (if (functionp code-component)
  3222.                   (fun-code code-component)
  3223.                   code-component)
  3224.               (c:backend-disassem-params backend))))
  3225.      (segments
  3226.       (get-code-segments dstate)))
  3227.     (when use-labels
  3228.       (label-segments segments dstate))
  3229.     (disassemble-segments segments stream dstate)))
  3230.  
  3231. ;;; ----------------------------------------------------------------
  3232. ;;; some handy function for machine-dependent code to use...
  3233.  
  3234. (defun note (note dstate)
  3235.   "Store NOTE (which can be either a string or a function with a single
  3236.   stream argument) to be printed as an end-of-line comment after the current
  3237.   instruction is disassembled."
  3238.   (declare (type (or string function) note)
  3239.        (type disassem-state dstate))
  3240.   (push note (dstate-notes dstate)))
  3241.  
  3242. (defun prin1-short (thing stream)
  3243.   (with-print-restrictions
  3244.     (prin1 thing stream)))
  3245.  
  3246. (defun prin1-quoted-short (thing stream)
  3247.   (if (self-evaluating-p thing)
  3248.       (prin1-short thing stream)
  3249.       (prin1-short `',thing stream)))
  3250.  
  3251. (defun note-code-constant (byte-offset dstate)
  3252.   "Store a note about the lisp constant located BYTE-OFFSET bytes from the
  3253.   current code-component, to be printed as an end-of-line comment after the
  3254.   current instruction is disassembled."
  3255.   (declare (type fixnum byte-offset)
  3256.        (type disassem-state dstate))
  3257.   (multiple-value-bind (const valid)
  3258.       (get-code-constant byte-offset dstate)
  3259.     (when valid
  3260.       (note #'(lambda (stream)
  3261.         (disassem:prin1-quoted-short const stream))
  3262.         dstate))
  3263.     const))
  3264.  
  3265. (defun maybe-note-nil-indexed-symbol-slot-ref (nil-byte-offset dstate)
  3266.   "If the memory address located NIL-BYTE-OFFSET bytes from the constant NIL
  3267.   is a valid slot in a symbol, store a note describing which symbol and slot,
  3268.   to be printed as an end-of-line comment after the current instruction is
  3269.   disassembled.  Returns non-NIL iff a note was recorded."
  3270.   (declare (type fixnum nil-byte-offset)
  3271.        (type disassem-state dstate))
  3272.   (multiple-value-bind (symbol access-fun)
  3273.       (grok-nil-indexed-symbol-slot-ref nil-byte-offset)
  3274.     (when access-fun
  3275.       (note #'(lambda (stream)
  3276.         (prin1 (if (eq access-fun 'symbol-value)
  3277.                symbol
  3278.                `(,access-fun ',symbol))
  3279.                stream))
  3280.         dstate))
  3281.     access-fun))
  3282.  
  3283. (defun maybe-note-nil-indexed-object (nil-byte-offset dstate)
  3284.   "If the memory address located NIL-BYTE-OFFSET bytes from the constant NIL
  3285.   is a valid lisp object, store a note describing which symbol and slot, to
  3286.   be printed as an end-of-line comment after the current instruction is
  3287.   disassembled.  Returns non-NIL iff a note was recorded."
  3288.   (declare (type fixnum nil-byte-offset)
  3289.        (type disassem-state dstate))
  3290.   (let ((obj (get-nil-indexed-object nil-byte-offset)))
  3291.     (note #'(lambda (stream)
  3292.           (prin1-quoted-short obj stream))
  3293.       dstate)
  3294.     t))
  3295.  
  3296. (defun maybe-note-assembler-routine (address dstate)
  3297.   "If ADDRESS is the address of a primitive assembler routine, store a note
  3298.   describing which one, to be printed as an end-of-line comment after the
  3299.   current instruction is disassembled.  Returns non-NIL iff a note was
  3300.   recorded."
  3301.   (declare (type integer address)
  3302.        (type disassem-state dstate))
  3303.   (let ((name (find-assembler-routine address)))
  3304.     (unless (null name)
  3305.       (note #'(lambda (stream)
  3306.         (format stream "#x~8,'0x: Primitive ~s" address name))
  3307.         dstate))
  3308.     name))
  3309.  
  3310. (defun maybe-note-single-storage-ref (offset sc-name dstate)
  3311.   "If there's a valid mapping from OFFSET in the storage class SC-NAME to a
  3312.   source variable, make a note of the source-variable name, to be printed as
  3313.   an end-of-line comment after the current instruction is disassembled.
  3314.   Returns non-NIL iff a note was recorded."
  3315.   (declare (type fixnum offset)
  3316.        (type symbol sc-name)
  3317.        (type disassem-state dstate))
  3318.   (let ((storage-location
  3319.      (find-valid-storage-location offset sc-name dstate)))
  3320.     (when storage-location
  3321.       (note #'(lambda (stream)
  3322.         (princ (di:debug-variable-symbol
  3323.             (aref (storage-info-debug-variables
  3324.                    (dstate-storage-info dstate))
  3325.                   storage-location))
  3326.                stream))
  3327.         dstate)
  3328.       t)))
  3329.  
  3330. (defun maybe-note-associated-storage-ref (offset sc-name assoc-with dstate)
  3331.   "If there's a valid mapping from OFFSET in the storage class SC-NAME to a
  3332.   source variable, make a note equating ASSOC-WITH with the source-variable
  3333.   name, to be printed as an end-of-line comment after the current instruction
  3334.   is disassembled.  Returns non-NIL iff a note was recorded."
  3335.   (declare (type fixnum offset)
  3336.        (type symbol sc-name)
  3337.        (type (or symbol string) assoc-with)
  3338.        (type disassem-state dstate))
  3339.   (let ((storage-location
  3340.      (find-valid-storage-location offset sc-name dstate)))
  3341.     (when storage-location
  3342.       (note #'(lambda (stream)
  3343.         (format stream "~a = ~s"
  3344.             assoc-with
  3345.             (di:debug-variable-symbol
  3346.              (aref (dstate-debug-variables dstate)
  3347.                    storage-location))
  3348.                stream))
  3349.         dstate)
  3350.       t)))
  3351.  
  3352. ;;; ----------------------------------------------------------------
  3353. ;;; these should be somewhere else...
  3354.  
  3355. (defun get-error-name (errnum)
  3356.   (car (svref (c:backend-internal-errors c:*backend*) errnum)))
  3357.  
  3358.  
  3359. (defun get-sc-name (sc-offs)
  3360.   (c::location-print-name
  3361.    (c::make-random-tn :kind :normal
  3362.               :sc (svref (c::backend-sc-numbers c:*backend*)
  3363.                  (c:sc-offset-scn sc-offs))
  3364.               :offset (c:sc-offset-offset sc-offs))))
  3365.  
  3366. ;;; ----------------------------------------------------------------
  3367.  
  3368. (defun handle-break-args (error-parse-fun stream dstate)
  3369.   "When called from an error break instruction's :DISASSEM-CONTROL (or
  3370.   :DISASSEM-PRINTER) function, will correctly deal with printing the
  3371.   arguments to the break.
  3372.  
  3373.   ERROR-PARSE-FUN should be a function that accepts:
  3374.     1) a SYSTEM-AREA-POINTER
  3375.     2) a BYTE-OFFSET from the SAP to begin at
  3376.     3) optionally, LENGTH-ONLY, which if non-NIL, means to only return
  3377.        the byte length of the arguments (to avoid unnecessary consing)
  3378.   It should read information from the SAP starting at BYTE-OFFSET, and return
  3379.   four values:
  3380.     1) the error number
  3381.     2) the total length, in bytes, of the information
  3382.     3) a list of SC-OFFSETs of the locations of the error parameters
  3383.     4) a list of the length (as read from the SAP), in bytes, of each of the
  3384.        return-values." 
  3385.   (declare (type function error-parse-fun)
  3386.        (type (or null stream) stream)
  3387.        (type disassem-state dstate))
  3388.   (multiple-value-bind (errnum adjust sc-offsets lengths)
  3389.       (funcall error-parse-fun
  3390.            (dstate-segment-sap dstate)
  3391.            (- (dstate-nextpos dstate) (dstate-segment-start dstate))
  3392.            (null stream))
  3393.     (when stream
  3394.       (setf (dstate-curpos dstate)
  3395.         (dstate-nextpos dstate))
  3396.       (flet ((emit-err-arg (note)
  3397.            (let ((num (pop lengths)))
  3398.          (print-notes-and-newline stream dstate)
  3399.          (print-current-address stream dstate)
  3400.          (print-bytes num stream dstate)
  3401.          (incf (dstate-curpos dstate) num)
  3402.          (when note
  3403.            (note note dstate)))))
  3404.     (emit-err-arg nil)
  3405.     (emit-err-arg (symbol-name (get-error-name errnum)))
  3406.     (dolist (sc-offs sc-offsets)
  3407.       (emit-err-arg (get-sc-name sc-offs))))
  3408.       )
  3409.     (incf (dstate-nextpos dstate) adjust)
  3410.     ))
  3411.